From 0563433e482bf95a0e3610c3e9ccf8965ca90243 Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Fri, 23 Jan 2026 15:24:12 +0900 Subject: [PATCH 01/16] =?UTF-8?q?fix:=20SpeakingHandler=20getStringOrNull?= =?UTF-8?q?=20=EC=BB=B4=ED=8C=8C=EC=9D=BC=20=EC=97=90=EB=9F=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../serverless/domain/speaking/handler/SpeakingHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java index c4a04370..c4166ed8 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java @@ -135,7 +135,7 @@ private APIGatewayProxyResponseEvent handleReset(String userId, String body) { } JsonObject request = JsonParser.parseString(body).getAsJsonObject(); - String sessionId = getStringOrNull(request, "sessionId"); + String sessionId = request.has("sessionId") ? request.get("sessionId").getAsString() : null; if (sessionId == null || sessionId.isEmpty()) { return response(400, Map.of("error", "sessionId is required")); From 5f2bc1b942214309adfdde165ca8a9620a79e68d Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Fri, 23 Jan 2026 15:46:22 +0900 Subject: [PATCH 02/16] =?UTF-8?q?fix:=20Bedrock=20=ED=82=A4=EC=9B=8C?= =?UTF-8?q?=EB=93=9C(meaningKo=20=ED=8F=AC=ED=95=A8)=EB=A5=BC=20article?= =?UTF-8?q?=EC=97=90=20=EC=A0=80=EC=9E=A5=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../news/service/NewsAnalysisService.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/news/service/NewsAnalysisService.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/news/service/NewsAnalysisService.java index f13712f0..87da58f1 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/news/service/NewsAnalysisService.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/news/service/NewsAnalysisService.java @@ -58,11 +58,7 @@ public NewsArticle analyzeArticle(NewsArticle article) { article.setCefrLevel(cefrLevel); article.setLevel(mapCefrToLevel(cefrLevel)); - // 2. 핵심 단어 추출 (Comprehend) - List keywords = extractKeywords(content); - article.setKeywords(keywords); - - // 3. 3줄 요약 + 퀴즈 생성 (Bedrock - 한 번에 처리) + // 2. 3줄 요약 + 키워드 + 퀴즈 생성 (Bedrock - 한 번에 처리) AnalysisResult result = generateSummaryAndQuiz(content, cefrLevel); if (result.summary() != null) { article.setSummary(result.summary()); @@ -70,6 +66,15 @@ public NewsArticle analyzeArticle(NewsArticle article) { article.setQuiz(result.quiz()); article.setHighlightWords(result.highlightWords()); + // Bedrock 키워드 사용 (meaningKo 포함) + if (result.keywords() != null && !result.keywords().isEmpty()) { + article.setKeywords(result.keywords()); + } else { + // fallback: Comprehend로 키워드 추출 + List keywords = extractKeywords(content); + article.setKeywords(keywords); + } + // 4. GSI 키 설정 article.setGsi1pk("LEVEL#" + article.getLevel()); article.setGsi1sk(article.getPublishedAt()); @@ -233,7 +238,7 @@ private AnalysisResult generateSummaryAndQuiz(String content, String cefrLevel) return parseAnalysisResult(response); } catch (Exception e) { logger.error("요약/퀴즈 생성 실패", e); - return new AnalysisResult(null, new ArrayList<>(), new ArrayList<>()); + return new AnalysisResult(null, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } } @@ -319,7 +324,7 @@ private AnalysisResult parseAnalysisResult(String response) { }); } - return new AnalysisResult(summary, highlightWords, quiz); + return new AnalysisResult(summary, keywords, highlightWords, quiz); } private String extractJson(String response) { @@ -341,6 +346,7 @@ private String truncate(String text, int maxLength) { */ private record AnalysisResult( String summary, + List keywords, List highlightWords, List quiz ) {} From 9c2d671a264932db3cbc6b6089f1ce408a776714 Mon Sep 17 00:00:00 2001 From: hyein Heo <128613248+hye-inA@users.noreply.github.com> Date: Fri, 23 Jan 2026 23:13:54 +0900 Subject: [PATCH 03/16] =?UTF-8?q?feature=20:=20Speaking=20Table=20&=20Func?= =?UTF-8?q?tion=20template.yaml=20=ED=8C=8C=EC=9D=BC=EC=97=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#513)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records * feat : Speaking 관련 template 람다 함수 및 테이블 추가 --------- Co-authored-by: DDING JOO --- .../handler/SpeakingConnectHandler.java | 0 .../handler/SpeakingDisconnectHandler.java | 0 .../speaking/handler/SpeakingHandler.java | 271 +++++++++--------- .../handler/SpeakingMessageHandler.java | 0 .../SpeakingConnectionRepository.java | 0 .../repository/SpeakingSessionRepository.java | 116 ++++---- ServerlessFunction/template.yaml | 78 +++++ 7 files changed, 273 insertions(+), 192 deletions(-) delete mode 100644 ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingConnectHandler.java delete mode 100644 ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingDisconnectHandler.java delete mode 100644 ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingMessageHandler.java delete mode 100644 ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/repository/SpeakingConnectionRepository.java diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingConnectHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingConnectHandler.java deleted file mode 100644 index e69de29b..00000000 diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingDisconnectHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingDisconnectHandler.java deleted file mode 100644 index e69de29b..00000000 diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java index ed6fbda0..2ebda605 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java @@ -19,142 +19,145 @@ /** * Speaking API 핸들러 - *

+ * * POST /api/speaking/chat - 대화 (음성 또는 텍스트) * POST /api/speaking/reset - 대화 초기화 */ public class SpeakingHandler implements RequestHandler { - - private static final Logger logger = LoggerFactory.getLogger(SpeakingHandler.class); - private static final Gson gson = new GsonBuilder().create(); - - private static final Map CORS_HEADERS = Map.of( - "Content-Type", "application/json", - "Access-Control-Allow-Origin", "*", - "Access-Control-Allow-Headers", "Content-Type,Authorization", - "Access-Control-Allow-Methods", "POST,OPTIONS" - ); - - private final SpeakingService speakingService; - - public SpeakingHandler() { - this.speakingService = new SpeakingService(); - } - - @Override - public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) { - logger.info("Speaking API request received"); - - // OPTIONS 요청 처리 (CORS preflight) - if ("OPTIONS".equalsIgnoreCase(event.getHttpMethod())) { - return response(200, Map.of("message", "OK")); - } - - try { - // JWT 토큰 검증 - String authHeader = event.getHeaders().get("Authorization"); - if (authHeader == null || !authHeader.startsWith("Bearer ")) { - return response(401, Map.of("error", "Authorization header is required")); - } - - String token = authHeader.substring(7); - if (!JwtUtil.isValid(token)) { - return response(401, Map.of("error", "Invalid or expired token")); - } - - Optional userIdOpt = JwtUtil.extractUserId(token); - if (userIdOpt.isEmpty()) { - return response(401, Map.of("error", "Invalid token")); - } - - String userId = userIdOpt.get(); - String path = event.getPath(); - String body = event.getBody(); - - logger.info("Processing request: path={}, userId={}", path, userId); - - // 라우팅 - if (path.endsWith("/chat")) { - return handleChat(userId, body); - } else if (path.endsWith("/reset")) { - return handleReset(userId, body); - } else { - return response(404, Map.of("error", "Not found")); - } - - } catch (Exception e) { - logger.error("Error processing request: {}", e.getMessage(), e); - return response(500, Map.of("error", "Internal server error: " + e.getMessage())); - } - } - - /** - * 대화 처리 (음성 또는 텍스트) - */ - private APIGatewayProxyResponseEvent handleChat(String userId, String body) { - if (body == null || body.isEmpty()) { - return response(400, Map.of("error", "Request body is required")); - } - - JsonObject request = JsonParser.parseString(body).getAsJsonObject(); - - String sessionId = request.has("sessionId") ? request.get("sessionId").getAsString() : null; - String level = request.has("level") ? request.get("level").getAsString() : "INTERMEDIATE"; - String audio = request.has("audio") ? request.get("audio").getAsString() : null; - String text = request.has("text") ? request.get("text").getAsString() : null; - - SpeakingResponse result; - - if (audio != null && !audio.isEmpty()) { - // 음성 입력 처리 - logger.info("Processing voice input"); - result = speakingService.processVoiceInput(sessionId, userId, audio, level); - } else if (text != null && !text.trim().isEmpty()) { - // 텍스트 입력 처리 - logger.info("Processing text input: {}", text); - result = speakingService.processTextInput(sessionId, userId, text.trim(), level); - } else { - return response(400, Map.of("error", "Either 'audio' or 'text' is required")); - } - - return response(200, Map.of( - "sessionId", result.sessionId(), - "userTranscript", result.userTranscript(), - "aiText", result.aiText(), - "aiAudioUrl", result.aiAudioUrl(), - "confidence", result.confidence() - )); - } - - /** - * 대화 초기화 - */ - private APIGatewayProxyResponseEvent handleReset(String userId, String body) { - if (body == null || body.isEmpty()) { - return response(400, Map.of("error", "Request body is required")); - } - - JsonObject request = JsonParser.parseString(body).getAsJsonObject(); - String sessionId = request.has("sessionId") ? request.get("sessionId").getAsString() : null; - - if (sessionId == null || sessionId.isEmpty()) { - return response(400, Map.of("error", "sessionId is required")); - } - - speakingService.resetConversation(sessionId); - - return response(200, Map.of( - "message", "Conversation reset successfully", - "sessionId", sessionId - )); - } - - private APIGatewayProxyResponseEvent response(int statusCode, Map body) { - return new APIGatewayProxyResponseEvent() - .withStatusCode(statusCode) - .withHeaders(CORS_HEADERS) - .withBody(gson.toJson(body)); - } - - + + private static final Logger logger = LoggerFactory.getLogger(SpeakingHandler.class); + private static final Gson gson = new GsonBuilder().create(); + + private static final Map CORS_HEADERS = Map.of( + "Content-Type", "application/json", + "Access-Control-Allow-Origin", "*", + "Access-Control-Allow-Headers", "Content-Type,Authorization", + "Access-Control-Allow-Methods", "POST,OPTIONS" + ); + + private final SpeakingService speakingService; + + public SpeakingHandler() { + this.speakingService = new SpeakingService(); + } + + @Override + public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent event, Context context) { + logger.info("Speaking API request received"); + + // OPTIONS 요청 처리 (CORS preflight) + if ("OPTIONS".equalsIgnoreCase(event.getHttpMethod())) { + return response(200, Map.of("message", "OK")); + } + + try { + // 사용자 인증 정보 추출 (Cognito Authorizer -> requestContext) + if (event.getRequestContext() == null || event.getRequestContext().getAuthorizer() == null) { + logger.error("No Authorizer found in request context"); + return response(401, Map.of("error", "Unauthorized: User context missing")); + } + + Map authorizer = event.getRequestContext().getAuthorizer(); + Map claims = (Map) authorizer.get("claims"); + + if (claims == null) { + return response(401, Map.of("error", "Unauthorized: Claims missing")); + } + + String userId = (String) claims.get("sub"); // Cognito User Pool의 고유 ID (UUID 형태) + + // 요청 정보 추출 + String path = event.getPath(); + String body = event.getBody(); + + logger.info("Processing request: path={}, userId={}", path, userId); + + // 라우팅 + if (path != null && path.endsWith("/chat")) { + return handleChat(userId, body); + } else if (path != null && path.endsWith("/reset")) { + return handleReset(userId, body); + } else { + return response(404, Map.of("error", "Not found")); + } + + } catch (Exception e) { + logger.error("Error processing request: {}", e.getMessage(), e); + return response(500, Map.of("error", "Internal server error: " + e.getMessage())); + } + } + + /** + * 대화 처리 (음성 또는 텍스트) + */ + private APIGatewayProxyResponseEvent handleChat(String userId, String body) { + if (body == null || body.isEmpty()) { + return response(400, Map.of("error", "Request body is required")); + } + + JsonObject request = JsonParser.parseString(body).getAsJsonObject(); + + String sessionId = request.has("sessionId") && !request.get("sessionId").isJsonNull() + ? request.get("sessionId").getAsString() : null; + String level = request.has("level") && !request.get("level").isJsonNull() + ? request.get("level").getAsString() : "INTERMEDIATE"; + String audio = request.has("audio") && !request.get("audio").isJsonNull() + ? request.get("audio").getAsString() : null; + String text = request.has("text") && !request.get("text").isJsonNull() + ? request.get("text").getAsString() : null; + + SpeakingResponse result; + + if (audio != null && !audio.isEmpty()) { + // 음성 입력 처리 + logger.info("Processing voice event"); + result = speakingService.processVoiceInput(sessionId, userId, audio, level); + } else if (text != null && !text.trim().isEmpty()) { + // 텍스트 입력 처리 + logger.info("Processing text event: {}", text); + result = speakingService.processTextInput(sessionId, userId, text.trim(), level); + } else { + return response(400, Map.of("error", "Either 'audio' or 'text' is required")); + } + + return response(200, Map.of( + "sessionId", result.sessionId(), + "userTranscript", result.userTranscript(), + "aiText", result.aiText(), + "aiAudioUrl", result.aiAudioUrl(), + "confidence", result.confidence() + )); + } + + /** + * 대화 초기화 + */ + private APIGatewayProxyResponseEvent handleReset(String userId, String body) { + if (body == null || body.isEmpty()) { + return response(400, Map.of("error", "Request body is required")); + } + + JsonObject request = JsonParser.parseString(body).getAsJsonObject(); + String sessionId = request.has("sessionId") ? request.get("sessionId").getAsString() : null; + + if (sessionId == null || sessionId.isEmpty()) { + return response(400, Map.of("error", "sessionId is required")); + } + + speakingService.resetConversation(sessionId); + + return response(200, Map.of( + "message", "Conversation reset successfully", + "sessionId", sessionId + )); + } + + private APIGatewayProxyResponseEvent response(int statusCode, Map body) { + return new APIGatewayProxyResponseEvent() + .withStatusCode(statusCode) + .withHeaders(CORS_HEADERS) + .withBody(gson.toJson(body)); + } + + } diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingMessageHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingMessageHandler.java deleted file mode 100644 index e69de29b..00000000 diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/repository/SpeakingConnectionRepository.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/repository/SpeakingConnectionRepository.java deleted file mode 100644 index e69de29b..00000000 diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/repository/SpeakingSessionRepository.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/repository/SpeakingSessionRepository.java index aa7acb63..dadda7e7 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/repository/SpeakingSessionRepository.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/repository/SpeakingSessionRepository.java @@ -12,63 +12,63 @@ import java.util.Optional; /** - * Speaking WebSocket 연결 정보 Repository + * Speaking API 연결 정보 Repository */ public class SpeakingSessionRepository { - - private static final Logger logger = LoggerFactory.getLogger(SpeakingSessionRepository.class); - private static final String TABLE_NAME = EnvConfig.getRequired("CHAT_TABLE_NAME"); - - private final DynamoDbTable table; - - public SpeakingSessionRepository() { - this.table = AwsClients.dynamoDbEnhanced().table( - TABLE_NAME, - TableSchema.fromBean(SpeakingSession.class) - ); - } - - /** - * 연결 정보 저장 - */ - public void save(SpeakingSession session) { - table.putItem(session); - logger.debug("Speaking session saved: sessionId={}, userId={}", - session.getSessionId(), session.getUserId()); - } - - /** - * sessionId로 연결 정보 조회 - */ - public Optional findBySessionId(String sessionId) { - Key key = Key.builder() - .partitionValue(SpeakingSession.PK_PREFIX + sessionId) - .sortValue(SpeakingSession.SK_METADATA) - .build(); - - SpeakingSession session = table.getItem(key); - return Optional.ofNullable(session); - } - - /** - * 연결 정보 업데이트 (대화 히스토리 등) - */ - public void update(SpeakingSession session) { - session.touch(); // 업데이트 시간 및 TTL 갱신 - table.putItem(session); - logger.debug("Speaking session updated: sessionId={}", session.getSessionId()); - } - - /** - * 연결 정보 삭제 - */ - public void delete(String sessionId) { - Key key = Key.builder() - .partitionValue(SpeakingSession.PK_PREFIX + sessionId) - .sortValue(SpeakingSession.SK_METADATA) - .build(); - - table.deleteItem(key); - logger.info("Speaking session deleted: sessionId={}", sessionId); - } -} + + private static final Logger logger = LoggerFactory.getLogger(SpeakingSessionRepository.class); + private static final String TABLE_NAME = EnvConfig.getRequired("SPEAKING_TABLE_NAME"); + + private final DynamoDbTable table; + + public SpeakingSessionRepository() { + this.table = AwsClients.dynamoDbEnhanced().table( + TABLE_NAME, + TableSchema.fromBean(SpeakingSession.class) + ); + } + + /** + * 연결 정보 저장 + */ + public void save(SpeakingSession session) { + table.putItem(session); + logger.debug("Speaking session saved: sessionId={}, userId={}", + session.getSessionId(), session.getUserId()); + } + + /** + * sessionId로 연결 정보 조회 + */ + public Optional findBySessionId(String sessionId) { + Key key = Key.builder() + .partitionValue(SpeakingSession.PK_PREFIX + sessionId) + .sortValue(SpeakingSession.SK_METADATA) + .build(); + + SpeakingSession session = table.getItem(key); + return Optional.ofNullable(session); + } + + /** + * 연결 정보 업데이트 (대화 히스토리 등) + */ + public void update(SpeakingSession session) { + session.touch(); // 업데이트 시간 및 TTL 갱신 + table.putItem(session); + logger.debug("Speaking session updated: sessionId={}", session.getSessionId()); + } + + /** + * 연결 정보 삭제 + */ + public void delete(String sessionId) { + Key key = Key.builder() + .partitionValue(SpeakingSession.PK_PREFIX + sessionId) + .sortValue(SpeakingSession.SK_METADATA) + .build(); + + table.deleteItem(key); + logger.info("Speaking session deleted: sessionId={}", sessionId); + } +} \ No newline at end of file diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index 3fa504b5..50fbb74d 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -37,6 +37,7 @@ Globals: VOCAB_TABLE_NAME: !Ref VocabTable OPIC_TABLE_NAME: !Ref OPIcTable NEWS_TABLE_NAME: !Ref NewsTable + SPEAKING_TABLE_NAME: !Ref SpeakingTable BUCKET_NAME: !Sub "${AWS::StackName}" CHAT_BUCKET_NAME: !Sub "${AWS::StackName}" VOCAB_BUCKET_NAME: !Sub "${AWS::StackName}" @@ -1551,6 +1552,47 @@ Resources: Auth: Authorizer: CognitoAuthorizer + ############################################# + # Speaking Lambda Functions + ############################################# + + SpeakingFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: !Sub "${AWS::StackName}-speaking-handler" + CodeUri: . + Handler: com.mzc.secondproject.serverless.domain.speaking.handler.SpeakingHandler::handleRequest + Description: Handle speaking chat API + SnapStart: + ApplyOn: PublishedVersions + Policies: + - DynamoDBCrudPolicy: + TableName: !Ref SpeakingTable + - S3CrudPolicy: + BucketName: !Sub "${AWS::StackName}" + - Statement: + - Effect: Allow + Action: + - bedrock:InvokeModel + Resource: "*" + Events: + SpeakingChat: + Type: Api + Properties: + RestApiId: !Ref MainApi + Path: /speaking/chat + Method: POST + Auth: + Authorizer: CognitoAuthorizer + SpeakingReset: + Type: Api + Properties: + RestApiId: !Ref MainApi + Path: /speaking/reset + Method: POST + Auth: + Authorizer: CognitoAuthorizer + ############################################# # DynamoDB Tables ############################################# @@ -1935,6 +1977,38 @@ Resources: AttributeName: ttl Enabled: true + SpeakingTable: + Type: AWS::DynamoDB::Table + Properties: + TableName: !Sub "${AWS::StackName}-speaking" + BillingMode: PAY_PER_REQUEST + AttributeDefinitions: + - AttributeName: PK + AttributeType: S + - AttributeName: SK + AttributeType: S + - AttributeName: GSI1PK + AttributeType: S + - AttributeName: GSI1SK + AttributeType: S + KeySchema: + - AttributeName: PK + KeyType: HASH + - AttributeName: SK + KeyType: RANGE + GlobalSecondaryIndexes: + - IndexName: GSI1 + KeySchema: + - AttributeName: GSI1PK + KeyType: HASH + - AttributeName: GSI1SK + KeyType: RANGE + Projection: + ProjectionType: ALL + TimeToLiveSpecification: + AttributeName: ttl + Enabled: true + ############################################# # S3 Bucket for Content Storage ############################################# @@ -2164,6 +2238,10 @@ Outputs: Description: OPIc DynamoDB Table Name Value: !Ref OPIcTable + SpeakingTableName: + Description: Speaking DynamoDB Table Name + Value: !Ref SpeakingTable + NotificationStreamUrl: Description: Notification SSE Stream Function URL Value: !GetAtt NotificationStreamFunctionUrl.FunctionUrl From 4cae51e1cdaf910f7469f11920f98968a332adfa Mon Sep 17 00:00:00 2001 From: hyein Heo <128613248+hye-inA@users.noreply.github.com> Date: Sat, 24 Jan 2026 00:40:38 +0900 Subject: [PATCH 04/16] =?UTF-8?q?feature=20:=20=EB=A7=90=ED=95=98=EA=B8=B0?= =?UTF-8?q?=20=EC=97=B0=EC=8A=B5=20=EA=B8=B0=EB=8A=A5=20polly=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EA=B6=8C=ED=95=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#514)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records * feat : Speaking 관련 template 람다 함수 및 테이블 추가 * feat : 말하기 기능에 polly 서비스 권한 추가 --------- Co-authored-by: DDING JOO --- ServerlessFunction/template.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index 50fbb74d..c0a7be9b 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -44,6 +44,7 @@ Globals: PROFILE_BUCKET_NAME: !Sub "${AWS::StackName}" OPIC_BUCKET_NAME: !Sub "${AWS::StackName}" NEWS_BUCKET_NAME: !Sub "${AWS::StackName}" + SPEAKING_BUCKET_NAME: !Sub "${AWS::StackName}" AWS_REGION_NAME: !Ref AWS::Region ROOM_TOKEN_TTL_SECONDS: "300" TRANSCRIBE_PROXY_URL: "https://tfo1zm7vec.execute-api.ap-northeast-2.amazonaws.com/prod/transcribe" @@ -1574,6 +1575,7 @@ Resources: - Effect: Allow Action: - bedrock:InvokeModel + - polly:SynthesizeSpeech Resource: "*" Events: SpeakingChat: From 7986bd1f4749d3a9d33a19025eefb56fd78ba067 Mon Sep 17 00:00:00 2001 From: hyein Heo <128613248+hye-inA@users.noreply.github.com> Date: Sat, 24 Jan 2026 01:06:18 +0900 Subject: [PATCH 05/16] =?UTF-8?q?feature=20:=20transcribe=20API=20KEY=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=20(#516)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records * feat : Speaking 관련 template 람다 함수 및 테이블 추가 * feat : 말하기 기능에 polly 서비스 권한 추가 * feat : transcribe API KEY 추가 --------- Co-authored-by: DDING JOO --- ServerlessFunction/template.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index c0a7be9b..857336c8 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -1566,11 +1566,19 @@ Resources: Description: Handle speaking chat API SnapStart: ApplyOn: PublishedVersions + Environment: + Variables: + TRANSCRIBE_API_KEY: "/opic/transcribe-proxy-api-key" Policies: - DynamoDBCrudPolicy: TableName: !Ref SpeakingTable - S3CrudPolicy: BucketName: !Sub "${AWS::StackName}" + - Statement: + - Effect: Allow + Action: + - ssm:GetParameter + Resource: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/opic/*" - Statement: - Effect: Allow Action: From 2ed6c0b22de7bb2a0de51a49e4d37cd7e9f0c524 Mon Sep 17 00:00:00 2001 From: hyein Heo <128613248+hye-inA@users.noreply.github.com> Date: Sun, 25 Jan 2026 02:46:50 +0900 Subject: [PATCH 06/16] =?UTF-8?q?feature=20:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=EC=97=90=EC=84=9C=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20(#535)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: add Bedrock permission to NewsCollectionFunction NewsCollectionFunction needs bedrock:InvokeModel permission to analyze news difficulty using AI. * fix: fallback to yesterday's news when today's news is empty GET /news now returns yesterday's articles if no articles exist for today. This prevents empty results before the daily 18:00 KST news collection runs. * feature : 채팅 도메인 닉네임 조회용 메서드 추가 (#534) * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방… --------- Co-authored-by: ddingjoo --- .../websocket/WebSocketMessageHandler.java | 20 + .../domain/chatting/model/ChatMessage.java | 1 + .../domain/news/service/NewsQueryService.java | 14 +- .../domain/user/service/UserService.java | 10 + ServerlessFunction/template.yaml | 5 + ...frontend-notification-integration-guide.md | 747 ++++++++++++++++++ docs/frontend-wordchain-guide.md | 365 +++++++++ 7 files changed, 1160 insertions(+), 2 deletions(-) create mode 100644 docs/frontend-notification-integration-guide.md create mode 100644 docs/frontend-wordchain-guide.md diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java index 5515cc1b..0435d5cb 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java @@ -19,6 +19,8 @@ import com.mzc.secondproject.serverless.domain.chatting.service.ChatMessageService; import com.mzc.secondproject.serverless.domain.chatting.service.CommandService; import com.mzc.secondproject.serverless.domain.chatting.service.GameService; +import com.mzc.secondproject.serverless.domain.user.model.User; +import com.mzc.secondproject.serverless.domain.user.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +46,7 @@ public class WebSocketMessageHandler implements RequestHandler handleRegularMessage(String connectionId, MessagePay // 일반 메시지 저장 및 브로드캐스트 String messageId = UUID.randomUUID().toString(); String now = Instant.now().toString(); + + // 닉네임 조회 + String nickname = "Unknown"; + try { + // DB에서 유저 정보(닉네임) 가져오기 + User user = userService.getUserProfile(payload.userId); + if (user != null && user.getNickname() != null) { + nickname = user.getNickname(); + } else { + // 혹시 없으면 UUID 사용 + nickname = payload.userId; + } + } catch (Exception e) { + nickname = payload.userId; + } ChatMessage message = ChatMessage.builder() .pk("ROOM#" + payload.roomId) @@ -166,6 +185,7 @@ private Map handleRegularMessage(String connectionId, MessagePay .messageId(messageId) .roomId(payload.roomId) .userId(payload.userId) + .nickname(nickname) .content(payload.content) .messageType(messageType) .createdAt(now) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/ChatMessage.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/ChatMessage.java index 0cbde348..211abaf1 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/ChatMessage.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/ChatMessage.java @@ -23,6 +23,7 @@ public class ChatMessage { private String messageId; private String roomId; private String userId; + private String nickname; private String content; private String messageType; // TEXT, IMAGE, VOICE, AI_RESPONSE private String createdAt; diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/news/service/NewsQueryService.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/news/service/NewsQueryService.java index c1a0f328..99f0e0ac 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/news/service/NewsQueryService.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/news/service/NewsQueryService.java @@ -40,12 +40,22 @@ public Optional getArticle(String articleId) { } /** - * 오늘의 뉴스 목록 조회 + * 오늘의 뉴스 목록 조회 (오늘 기사 없으면 어제 기사 조회) */ public PaginatedResult getTodayNews(int limit, String cursor) { String today = LocalDate.now().toString(); logger.debug("오늘의 뉴스 조회: date={}, limit={}", today, limit); - return articleRepository.findByDate(today, limit, cursor); + + PaginatedResult result = articleRepository.findByDate(today, limit, cursor); + + // 오늘 기사가 없으면 어제 기사 조회 + if (result.items().isEmpty() && cursor == null) { + String yesterday = LocalDate.now().minusDays(1).toString(); + logger.debug("오늘 기사 없음, 어제 기사 조회: date={}", yesterday); + result = articleRepository.findByDate(yesterday, limit, cursor); + } + + return result; } /** diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/service/UserService.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/service/UserService.java index 4f635106..e302205b 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/service/UserService.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/service/UserService.java @@ -31,11 +31,16 @@ public class UserService { private static final int NICKNAME_MAX_LENGTH = 20; private final UserRepository userRepository; private final S3Presigner s3Presigner; + public UserService(UserRepository userRepository) { this.userRepository = userRepository; // AwsClients 싱글톤 사용 - Cold Start 최적화 this.s3Presigner = AwsClients.s3Presigner(); } + + public UserService() { + this(new UserRepository()); + } private static String getDefaultProfileUrl() { String bucket = BUCKET_NAME != null ? BUCKET_NAME : "group2-englishstudy"; @@ -66,6 +71,11 @@ public User getProfile(String userId, APIGatewayProxyRequestEvent request) { return user; } + + // 단순 프로필 조회 메서드 (채팅용) - DB 조회만 수행 + public User getUserProfile(String userId) { + return userRepository.findByCognitoSub(userId).orElse(null); + } public String getPresignedProfileUrl(String s3Url) { if (s3Url == null || s3Url.isEmpty()) { diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index 0c3dea39..225dc206 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -1871,6 +1871,11 @@ Resources: Policies: - DynamoDBCrudPolicy: TableName: !Ref NewsTable + - Statement: + - Effect: Allow + Action: + - bedrock:InvokeModel + Resource: "*" Events: DailySchedule: Type: Schedule diff --git a/docs/frontend-notification-integration-guide.md b/docs/frontend-notification-integration-guide.md new file mode 100644 index 00000000..5c647bcf --- /dev/null +++ b/docs/frontend-notification-integration-guide.md @@ -0,0 +1,747 @@ +# 프론트엔드 실시간 알림 연동 가이드 + +## 개요 + +이 문서는 백엔드 알림 시스템과 프론트엔드를 연동하기 위한 가이드입니다. +**Server-Sent Events (SSE)** 를 사용하여 실시간 알림을 수신합니다. + +--- + +## 연결 방식 + +### SSE (Server-Sent Events) 사용 + +- WebSocket과 달리 **단방향 통신** (서버 → 클라이언트) +- HTTP 기반으로 별도 프로토콜 핸들링 불필요 +- 브라우저 `EventSource` API로 간단히 구현 가능 +- 연결 끊김 시 자동 재연결 지원 + +--- + +## 연결 엔드포인트 + +``` +GET {NOTIFICATION_FUNCTION_URL}?userId={userId} +``` + +| 파라미터 | 설명 | 예시 | +|---------|------|------| +| `userId` | 로그인한 사용자 ID | `user-123` | + +> ⚠️ **NOTIFICATION_FUNCTION_URL**은 배포 환경별로 다릅니다. 환경변수로 관리하세요. + +--- + +## 기본 연결 구현 + +### JavaScript (Vanilla) + +```javascript +const connectNotifications = (userId) => { + const url = `${NOTIFICATION_FUNCTION_URL}?userId=${userId}`; + const eventSource = new EventSource(url); + + // 알림 수신 + eventSource.onmessage = (event) => { + const notification = JSON.parse(event.data); + handleNotification(notification); + }; + + // 연결 성공 + eventSource.onopen = () => { + console.log('알림 연결 성공'); + }; + + // 에러 처리 + eventSource.onerror = (error) => { + console.error('알림 연결 에러:', error); + // EventSource는 자동으로 재연결을 시도합니다 + }; + + return eventSource; +}; + +// 연결 해제 +const disconnect = (eventSource) => { + eventSource.close(); +}; +``` + +### React Hook 예시 + +```typescript +import { useEffect, useCallback, useRef } from 'react'; + +interface Notification { + notificationId: string; + type: NotificationType; + userId: string; + payload: Record; + createdAt: string; +} + +type NotificationType = + | 'BADGE_EARNED' + | 'DAILY_COMPLETE' + | 'STREAK_REMINDER' + | 'TEST_COMPLETE' + | 'NEWS_QUIZ_COMPLETE' + | 'GAME_END' + | 'GAME_STREAK' + | 'OPIC_COMPLETE'; + +export const useNotifications = ( + userId: string | null, + onNotification: (notification: Notification) => void +) => { + const eventSourceRef = useRef(null); + + const connect = useCallback(() => { + if (!userId) return; + + const url = `${process.env.NEXT_PUBLIC_NOTIFICATION_URL}?userId=${userId}`; + const eventSource = new EventSource(url); + + eventSource.onmessage = (event) => { + // Heartbeat 무시 + if (event.data === 'HEARTBEAT') return; + + try { + const notification: Notification = JSON.parse(event.data); + onNotification(notification); + } catch (e) { + console.error('알림 파싱 실패:', e); + } + }; + + eventSource.onerror = () => { + console.log('알림 연결 끊김, 재연결 시도 중...'); + }; + + eventSourceRef.current = eventSource; + }, [userId, onNotification]); + + const disconnect = useCallback(() => { + if (eventSourceRef.current) { + eventSourceRef.current.close(); + eventSourceRef.current = null; + } + }, []); + + useEffect(() => { + connect(); + return () => disconnect(); + }, [connect, disconnect]); + + return { disconnect, reconnect: connect }; +}; +``` + +### React 컴포넌트 사용 예시 + +```tsx +const NotificationProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const { user } = useAuth(); + const [notifications, setNotifications] = useState([]); + + const handleNotification = useCallback((notification: Notification) => { + setNotifications(prev => [notification, ...prev]); + + // 타입별 처리 + switch (notification.type) { + case 'BADGE_EARNED': + showBadgeToast(notification.payload); + break; + case 'DAILY_COMPLETE': + showStreakCelebration(notification.payload); + break; + case 'GAME_END': + showGameResult(notification.payload); + break; + // ... 기타 타입 + } + }, []); + + useNotifications(user?.id ?? null, handleNotification); + + return ( + + {children} + + ); +}; +``` + +--- + +## 알림 타입 및 Payload 구조 + +### 공통 응답 구조 + +```typescript +interface Notification { + notificationId: string; // "notif-xxxxxxxx" 형식 + type: NotificationType; // 알림 타입 + userId: string; // 대상 사용자 ID + payload: object; // 타입별 상세 데이터 + createdAt: string; // ISO-8601 형식 (예: "2024-01-15T09:30:00Z") +} +``` + +--- + +### 1. BADGE_EARNED (배지 획득) + +사용자가 새로운 배지를 획득했을 때 + +```typescript +interface BadgeEarnedPayload { + badgeType: string; // 배지 타입 코드 + badgeName: string; // 배지 이름 + description: string; // 배지 설명 + iconUrl: string; // 배지 아이콘 URL +} +``` + +**예시:** +```json +{ + "notificationId": "notif-a1b2c3d4", + "type": "BADGE_EARNED", + "userId": "user-123", + "payload": { + "badgeType": "STREAK_7", + "badgeName": "7일 연속 학습", + "description": "7일 연속으로 학습을 완료했습니다!", + "iconUrl": "https://cdn.example.com/badges/streak-7.png" + }, + "createdAt": "2024-01-15T09:30:00Z" +} +``` + +--- + +### 2. DAILY_COMPLETE (일일 학습 완료) + +오늘의 단어 학습을 모두 완료했을 때 + +```typescript +interface DailyCompletePayload { + date: string; // 학습 완료 날짜 (YYYY-MM-DD) + wordsLearned: number; // 오늘 학습한 단어 수 + totalWords: number; // 총 학습 단어 수 + currentStreak: number; // 현재 연속 학습 일수 +} +``` + +**예시:** +```json +{ + "notificationId": "notif-e5f6g7h8", + "type": "DAILY_COMPLETE", + "userId": "user-123", + "payload": { + "date": "2024-01-15", + "wordsLearned": 20, + "totalWords": 150, + "currentStreak": 5 + }, + "createdAt": "2024-01-15T14:00:00Z" +} +``` + +--- + +### 3. STREAK_REMINDER (연속 학습 리마인더) + +매일 21:00 KST에 오늘 학습을 아직 하지 않은 사용자에게 발송 + +```typescript +interface StreakReminderPayload { + currentStreak: number; // 현재 연속 학습 일수 + message: string; // 리마인더 메시지 +} +``` + +**예시:** +```json +{ + "notificationId": "notif-i9j0k1l2", + "type": "STREAK_REMINDER", + "userId": "user-123", + "payload": { + "currentStreak": 5, + "message": "오늘 학습을 완료하고 6일 연속 학습을 달성하세요!" + }, + "createdAt": "2024-01-15T12:00:00Z" +} +``` + +--- + +### 4. TEST_COMPLETE (단어 테스트 완료) + +단어 테스트를 완료했을 때 + +```typescript +interface TestCompletePayload { + testId: string; // 테스트 ID + score: number; // 점수 (0-100) + correctCount: number; // 맞힌 문제 수 + totalCount: number; // 전체 문제 수 + isPerfect: boolean; // 만점 여부 +} +``` + +**예시:** +```json +{ + "notificationId": "notif-m3n4o5p6", + "type": "TEST_COMPLETE", + "userId": "user-123", + "payload": { + "testId": "test-abc123", + "score": 85, + "correctCount": 17, + "totalCount": 20, + "isPerfect": false + }, + "createdAt": "2024-01-15T10:30:00Z" +} +``` + +--- + +### 5. NEWS_QUIZ_COMPLETE (뉴스 퀴즈 완료) + +뉴스 기사 퀴즈를 완료했을 때 + +```typescript +interface NewsQuizCompletePayload { + articleId: string; // 뉴스 기사 ID + articleTitle: string; // 기사 제목 + score: number; // 점수 (0-100) + correctCount: number; // 맞힌 문제 수 + totalCount: number; // 전체 문제 수 + isPerfect: boolean; // 만점 여부 +} +``` + +**예시:** +```json +{ + "notificationId": "notif-q7r8s9t0", + "type": "NEWS_QUIZ_COMPLETE", + "userId": "user-123", + "payload": { + "articleId": "article-xyz789", + "articleTitle": "Tech Giants Report Strong Q4 Earnings", + "score": 100, + "correctCount": 5, + "totalCount": 5, + "isPerfect": true + }, + "createdAt": "2024-01-15T11:00:00Z" +} +``` + +--- + +### 6. GAME_END (게임 종료) + +캐치마인드 게임이 종료되었을 때 + +```typescript +interface GameEndPayload { + roomId: string; // 게임 방 ID + gameSessionId: string; // 게임 세션 ID + rank: number; // 최종 순위 + totalPlayers: number; // 전체 플레이어 수 + score: number; // 획득 점수 + isWinner: boolean; // 1등 여부 +} +``` + +**예시:** +```json +{ + "notificationId": "notif-u1v2w3x4", + "type": "GAME_END", + "userId": "user-123", + "payload": { + "roomId": "room-game-001", + "gameSessionId": "session-abc", + "rank": 1, + "totalPlayers": 4, + "score": 2500, + "isWinner": true + }, + "createdAt": "2024-01-15T15:30:00Z" +} +``` + +--- + +### 7. GAME_STREAK (게임 연속 정답) + +게임 중 연속 정답을 달성했을 때 + +```typescript +interface GameStreakPayload { + roomId: string; // 게임 방 ID + streakCount: number; // 연속 정답 횟수 + bonusPoints: number; // 보너스 점수 +} +``` + +**예시:** +```json +{ + "notificationId": "notif-y5z6a7b8", + "type": "GAME_STREAK", + "userId": "user-123", + "payload": { + "roomId": "room-game-001", + "streakCount": 5, + "bonusPoints": 500 + }, + "createdAt": "2024-01-15T15:25:00Z" +} +``` + +--- + +### 8. OPIC_COMPLETE (OPIc 연습 완료) + +OPIc 스피킹 연습 세션을 완료했을 때 + +```typescript +interface OpicCompletePayload { + sessionId: string; // 세션 ID + estimatedLevel: string; // 예상 등급 (IM1, IM2, IH, AL 등) + questionsAnswered: number; // 답변한 문제 수 + feedbackSummary: string; // 피드백 요약 +} +``` + +**예시:** +```json +{ + "notificationId": "notif-c9d0e1f2", + "type": "OPIC_COMPLETE", + "userId": "user-123", + "payload": { + "sessionId": "opic-session-456", + "estimatedLevel": "IM2", + "questionsAnswered": 15, + "feedbackSummary": "발음과 유창성이 좋습니다. 문법적 정확성을 더 연습하세요." + }, + "createdAt": "2024-01-15T16:00:00Z" +} +``` + +--- + +## 특수 이벤트 + +### HEARTBEAT (하트비트) + +서버에서 연결 유지를 위해 1초마다 전송합니다. 무시하면 됩니다. + +```javascript +eventSource.onmessage = (event) => { + if (event.data === 'HEARTBEAT') return; // 무시 + // ... +}; +``` + +### STREAM_END (스트림 종료) + +서버가 연결을 종료할 때 전송됩니다. (최대 14분 후) +`EventSource`는 자동으로 재연결을 시도합니다. + +--- + +## 연결 관리 권장사항 + +### 1. 연결 시점 + +```typescript +// 로그인 후 연결 +const handleLoginSuccess = (user: User) => { + connectNotifications(user.id); +}; + +// 페이지 로드 시 (이미 로그인된 경우) +useEffect(() => { + if (isAuthenticated && user) { + connectNotifications(user.id); + } +}, [isAuthenticated, user]); +``` + +### 2. 연결 해제 시점 + +```typescript +// 로그아웃 시 +const handleLogout = () => { + disconnectNotifications(); + // ... +}; + +// 페이지 언마운트 시 (SPA) +useEffect(() => { + return () => disconnectNotifications(); +}, []); +``` + +### 3. 재연결 처리 + +`EventSource`는 연결 끊김 시 자동 재연결을 시도합니다. +추가적인 재연결 로직이 필요한 경우: + +```typescript +const MAX_RETRY_COUNT = 5; +let retryCount = 0; + +eventSource.onerror = () => { + retryCount++; + + if (retryCount >= MAX_RETRY_COUNT) { + eventSource.close(); + showErrorMessage('알림 서버 연결에 실패했습니다. 새로고침해주세요.'); + } +}; + +eventSource.onopen = () => { + retryCount = 0; // 연결 성공 시 초기화 +}; +``` + +--- + +## UI 처리 권장사항 + +### 토스트 알림 + +```typescript +const showNotificationToast = (notification: Notification) => { + const config = getToastConfig(notification.type); + + toast({ + title: config.title, + description: formatPayload(notification.payload), + icon: config.icon, + duration: config.duration, + }); +}; + +const getToastConfig = (type: NotificationType) => { + switch (type) { + case 'BADGE_EARNED': + return { title: '🏆 배지 획득!', icon: 'trophy', duration: 5000 }; + case 'DAILY_COMPLETE': + return { title: '✅ 오늘의 학습 완료!', icon: 'check', duration: 4000 }; + case 'STREAK_REMINDER': + return { title: '⏰ 학습 리마인더', icon: 'clock', duration: 6000 }; + case 'TEST_COMPLETE': + return { title: '📝 테스트 완료', icon: 'file', duration: 3000 }; + case 'GAME_END': + return { title: '🎮 게임 종료', icon: 'gamepad', duration: 4000 }; + default: + return { title: '알림', icon: 'bell', duration: 3000 }; + } +}; +``` + +### 알림 센터 + +```typescript +const NotificationCenter: React.FC = () => { + const { notifications } = useNotificationContext(); + const [unreadCount, setUnreadCount] = useState(0); + + return ( + + + + {unreadCount > 0 && } + + + {notifications.map(notif => ( + + ))} + + + ); +}; +``` + +--- + +## TypeScript 타입 정의 (복사용) + +```typescript +// types/notification.ts + +export type NotificationType = + | 'BADGE_EARNED' + | 'DAILY_COMPLETE' + | 'STREAK_REMINDER' + | 'TEST_COMPLETE' + | 'NEWS_QUIZ_COMPLETE' + | 'GAME_END' + | 'GAME_STREAK' + | 'OPIC_COMPLETE'; + +export interface BaseNotification { + notificationId: string; + type: T; + userId: string; + payload: P; + createdAt: string; +} + +export interface BadgeEarnedPayload { + badgeType: string; + badgeName: string; + description: string; + iconUrl: string; +} + +export interface DailyCompletePayload { + date: string; + wordsLearned: number; + totalWords: number; + currentStreak: number; +} + +export interface StreakReminderPayload { + currentStreak: number; + message: string; +} + +export interface TestCompletePayload { + testId: string; + score: number; + correctCount: number; + totalCount: number; + isPerfect: boolean; +} + +export interface NewsQuizCompletePayload { + articleId: string; + articleTitle: string; + score: number; + correctCount: number; + totalCount: number; + isPerfect: boolean; +} + +export interface GameEndPayload { + roomId: string; + gameSessionId: string; + rank: number; + totalPlayers: number; + score: number; + isWinner: boolean; +} + +export interface GameStreakPayload { + roomId: string; + streakCount: number; + bonusPoints: number; +} + +export interface OpicCompletePayload { + sessionId: string; + estimatedLevel: string; + questionsAnswered: number; + feedbackSummary: string; +} + +export type Notification = + | BaseNotification<'BADGE_EARNED', BadgeEarnedPayload> + | BaseNotification<'DAILY_COMPLETE', DailyCompletePayload> + | BaseNotification<'STREAK_REMINDER', StreakReminderPayload> + | BaseNotification<'TEST_COMPLETE', TestCompletePayload> + | BaseNotification<'NEWS_QUIZ_COMPLETE', NewsQuizCompletePayload> + | BaseNotification<'GAME_END', GameEndPayload> + | BaseNotification<'GAME_STREAK', GameStreakPayload> + | BaseNotification<'OPIC_COMPLETE', OpicCompletePayload>; +``` + +--- + +## 환경 설정 + +### 환경 변수 + +| 환경 | URL | +|------|-----| +| **Test** | `https://flhf42jd6xgrh26wrqgwxmbmee0zmjnv.lambda-url.ap-northeast-2.on.aws/` | +| **Prod** | (배포 후 업데이트 예정) | + +```env +# .env.local (Next.js) +NEXT_PUBLIC_NOTIFICATION_URL=https://flhf42jd6xgrh26wrqgwxmbmee0zmjnv.lambda-url.ap-northeast-2.on.aws + +# .env (Vite) +VITE_NOTIFICATION_URL=https://flhf42jd6xgrh26wrqgwxmbmee0zmjnv.lambda-url.ap-northeast-2.on.aws +``` + +--- + +## 테스트 방법 + +### 개발 환경에서 테스트 + +1. 브라우저 개발자 도구 → Network 탭 열기 +2. EventStream 필터 선택 +3. 로그인 후 알림 연결 확인 +4. 학습 완료, 테스트 제출 등의 액션 수행 +5. 실시간으로 알림 수신 확인 + +### Mock SSE 서버 (로컬 테스트용) + +```javascript +// mock-sse-server.js +const http = require('http'); + +http.createServer((req, res) => { + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + 'Access-Control-Allow-Origin': '*', + }); + + // 테스트 알림 전송 + setInterval(() => { + const notification = { + notificationId: `notif-${Date.now()}`, + type: 'BADGE_EARNED', + userId: 'test-user', + payload: { + badgeType: 'TEST_BADGE', + badgeName: '테스트 배지', + description: '테스트용 배지입니다', + iconUrl: 'https://example.com/badge.png', + }, + createdAt: new Date().toISOString(), + }; + res.write(`data: ${JSON.stringify(notification)}\n\n`); + }, 5000); +}).listen(3001); + +console.log('Mock SSE server running on http://localhost:3001'); +``` + +--- + +## 문의 + +백엔드 알림 시스템 관련 문의: **[백엔드 담당자 이름/연락처]** \ No newline at end of file diff --git a/docs/frontend-wordchain-guide.md b/docs/frontend-wordchain-guide.md new file mode 100644 index 00000000..5296aa09 --- /dev/null +++ b/docs/frontend-wordchain-guide.md @@ -0,0 +1,365 @@ +# 영어 끝말잇기(쿵쿵따) 프론트엔드 통합 가이드 + +## 개요 +영어 끝말잇기 게임 - 이전 단어의 마지막 글자로 시작하는 단어를 제출하는 게임 + +## REST API 엔드포인트 + +### 1. 게임 시작 +``` +POST /chat/rooms/{roomId}/wordchain/start +Authorization: Bearer {token} +``` + +**Response (성공):** +```json +{ + "success": true, + "message": "Word Chain game started", + "data": { + "sessionId": "uuid", + "gameStatus": "PLAYING", + "currentRound": 1, + "currentPlayerId": "user-id", + "currentWord": "apple", + "nextLetter": "e", + "timeLimit": 15, + "turnStartTime": 1706000000000, + "serverTime": 1706000000000, + "activePlayers": ["user1", "user2", "user3"], + "eliminatedPlayers": [], + "scores": {}, + "usedWords": ["apple"] + } +} +``` + +### 2. 단어 제출 +``` +POST /chat/rooms/{roomId}/wordchain/submit +Authorization: Bearer {token} +Content-Type: application/json + +{ + "word": "elephant" +} +``` + +**Response (정답):** +```json +{ + "success": true, + "message": "Correct!", + "data": { + "resultType": "CORRECT", + "word": "elephant", + "definition": "(noun) A large mammal with a trunk", + "phonetic": "/ˈɛləfənt/", + "score": 23, + "nextLetter": "t", + "nextPlayerId": "user2", + "nextTimeLimit": 15 + } +} +``` + +**Response (오답 - 첫 글자 틀림):** +```json +{ + "success": true, + "message": "Wrong answer", + "data": { + "resultType": "WRONG_LETTER", + "error": "'e'로 시작하는 단어를 입력하세요." + } +} +``` + +**Response (오답 - 사전에 없음):** +```json +{ + "success": true, + "message": "Wrong answer", + "data": { + "resultType": "INVALID_WORD", + "error": "사전에 없는 단어입니다: xyz" + } +} +``` + +### 3. 타임아웃 처리 +``` +POST /chat/rooms/{roomId}/wordchain/timeout +Authorization: Bearer {token} +``` + +### 4. 게임 종료 (시작자만) +``` +POST /chat/rooms/{roomId}/wordchain/stop +Authorization: Bearer {token} +``` + +### 5. 게임 상태 조회 +``` +GET /chat/rooms/{roomId}/wordchain/status +Authorization: Bearer {token} +``` + +--- + +## WebSocket 메시지 + +### Domain +```javascript +domain: "wordchain" +``` + +### 메시지 타입 + +| messageType | 설명 | +|-------------|------| +| `wordchain_start` | 게임 시작 | +| `wordchain_correct` | 정답 | +| `wordchain_wrong` | 오답 | +| `wordchain_timeout` | 시간 초과 (탈락) | +| `wordchain_end` | 게임 종료 | + +--- + +## WebSocket 메시지 상세 + +### 1. 게임 시작 (wordchain_start) +```json +{ + "domain": "wordchain", + "messageType": "wordchain_start", + "messageId": "uuid", + "roomId": "room-id", + "userId": "SYSTEM", + "content": "🎮 끝말잇기 시작!\n시작 단어: apple\n다음 글자: 'e'\n\n첫 번째 차례: user1\n제한 시간: 15초", + "createdAt": "2026-01-24T12:00:00Z", + "timestamp": 1706000000000, + "sessionId": "session-uuid", + "starterWord": "apple", + "nextLetter": "e", + "currentPlayerId": "user1", + "timeLimit": 15, + "turnStartTime": 1706000000000, + "serverTime": 1706000000000, + "players": ["user1", "user2", "user3"], + "activePlayers": ["user1", "user2", "user3"] +} +``` + +### 2. 정답 (wordchain_correct) +```json +{ + "domain": "wordchain", + "messageType": "wordchain_correct", + "messageId": "uuid", + "roomId": "room-id", + "userId": "SYSTEM", + "content": "✅ 닉네임: \"elephant\" (+23점)\n뜻: (noun) A large mammal\n다음 글자: 't'", + "createdAt": "2026-01-24T12:00:05Z", + "timestamp": 1706000005000, + "serverTime": 1706000005000, + "resultType": "CORRECT", + "word": "elephant", + "definition": "(noun) A large mammal with a trunk", + "phonetic": "/ˈɛləfənt/", + "score": 23, + "nextLetter": "t", + "nextPlayerId": "user2", + "nextTimeLimit": 15, + "playerNickname": "닉네임", + "turnStartTime": 1706000005000, + "scores": { + "user1": 23 + } +} +``` + +### 3. 오답 (wordchain_wrong) +```json +{ + "domain": "wordchain", + "messageType": "wordchain_wrong", + "messageId": "uuid", + "roomId": "room-id", + "userId": "SYSTEM", + "content": "❌ 사전에 없는 단어입니다: xyz", + "resultType": "INVALID_WORD", + "error": "사전에 없는 단어입니다: xyz" +} +``` + +### 4. 시간 초과 (wordchain_timeout) +```json +{ + "domain": "wordchain", + "messageType": "wordchain_timeout", + "messageId": "uuid", + "roomId": "room-id", + "userId": "SYSTEM", + "content": "⏰ 닉네임 시간 초과! 탈락!", + "resultType": "TIMEOUT", + "eliminatedPlayerId": "user1", + "eliminatedNickname": "닉네임", + "nextPlayerId": "user2", + "nextTimeLimit": 13, + "nextLetter": "e", + "turnStartTime": 1706000015000, + "activePlayers": ["user2", "user3"] +} +``` + +### 5. 게임 종료 (wordchain_end) +```json +{ + "domain": "wordchain", + "messageType": "wordchain_end", + "messageId": "uuid", + "roomId": "room-id", + "userId": "SYSTEM", + "content": "🏆 승자: 닉네임!", + "resultType": "GAME_END", + "winnerId": "user2", + "winnerNickname": "닉네임", + "ranking": [ + { "playerId": "user2", "nickname": "닉네임2", "score": 45, "eliminated": false }, + { "playerId": "user3", "nickname": "닉네임3", "score": 30, "eliminated": true }, + { "playerId": "user1", "nickname": "닉네임1", "score": 23, "eliminated": true } + ], + "usedWords": ["apple", "elephant", "tiger", "rainbow"], + "wordDefinitions": { + "apple": "(noun) A fruit", + "elephant": "(noun) A large mammal", + "tiger": "(noun) A large cat", + "rainbow": "(noun) An arc of colors" + }, + "scores": { + "user1": 23, + "user2": 45, + "user3": 30 + } +} +``` + +--- + +## 게임 규칙 + +### 시간 제한 (라운드별 감소) +| 라운드 | 시간 제한 | +|--------|----------| +| 1-2 | 15초 | +| 3-4 | 13초 | +| 5-6 | 11초 | +| 7-8 | 9초 | +| 9+ | 8초 | + +### 점수 계산 +``` +점수 = 기본점수(10) + 시간보너스 + 길이보너스 + +시간보너스 = 남은시간(초) +길이보너스 = (단어길이 - 4) × 2 (5글자 이상부터) +``` + +**예시:** +- 15초 제한에서 5초 만에 "elephant"(8글자) 제출 +- 점수 = 10 + 10 + 8 = 28점 + +### 게임 종료 조건 +- 1명만 남으면 게임 종료 +- 시작자가 `/stop` 호출 + +--- + +## 프론트엔드 구현 가이드 + +### 1. 타이머 동기화 +```javascript +// 서버 시간과 클라이언트 시간 차이 계산 +const serverTimeDiff = message.serverTime - Date.now(); + +// 남은 시간 계산 +const elapsed = Date.now() + serverTimeDiff - message.turnStartTime; +const remaining = (message.timeLimit * 1000) - elapsed; +``` + +### 2. WebSocket 메시지 핸들러 +```javascript +socket.onmessage = (event) => { + const message = JSON.parse(event.data); + + if (message.domain !== 'wordchain') return; + + switch (message.messageType) { + case 'wordchain_start': + handleGameStart(message); + break; + case 'wordchain_correct': + handleCorrectAnswer(message); + break; + case 'wordchain_wrong': + handleWrongAnswer(message); + break; + case 'wordchain_timeout': + handleTimeout(message); + break; + case 'wordchain_end': + handleGameEnd(message); + break; + } +}; +``` + +### 3. 타임아웃 자동 전송 +```javascript +// 내 턴일 때 타이머 만료 시 자동으로 타임아웃 API 호출 +if (isMyTurn && remaining <= 0) { + fetch(`/chat/rooms/${roomId}/wordchain/timeout`, { + method: 'POST', + headers: { 'Authorization': `Bearer ${token}` } + }); +} +``` + +### 4. UI 구성 요소 +- 현재 단어 표시 +- 다음 시작 글자 강조 +- 타이머 (남은 시간) +- 현재 차례 플레이어 표시 +- 활성/탈락 플레이어 목록 +- 점수판 +- 사용된 단어 목록 +- 단어 입력 필드 (본인 차례일 때만 활성화) + +### 5. 게임 종료 후 학습 화면 +```javascript +// 게임 종료 시 사용된 단어와 뜻 표시 +message.usedWords.forEach(word => { + const definition = message.wordDefinitions[word]; + console.log(`${word}: ${definition}`); +}); +``` + +--- + +## 에러 코드 + +| 코드 | 메시지 | +|------|--------| +| GAME_001 | 게임 시작에 실패했습니다 | +| GAME_002 | 게임 중단에 실패했습니다 | +| GAME_010 | 게임 액션 처리에 실패했습니다 | +| INPUT_001 | 유효하지 않은 입력입니다 | + +--- + +## 참고 + +- Dictionary API: [Free Dictionary API](https://dictionaryapi.dev/) +- 최소 인원: 2명 +- 시작 단어: 서버에서 랜덤 선택 (apple, house, water 등) From 0a87543309bf024389f1e87d12a79641651f7e7d Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Sun, 25 Jan 2026 18:14:41 +0900 Subject: [PATCH 07/16] fix: revert buildspec to build+package only, let Deploy stage handle deployment - Remove sam deploy from buildspec-test.yml and buildspec-prod.yml - CodeBuild only does sam build + sam package - Deploy stage handles CloudFormation deployment with Cognito parameters --- ServerlessFunction/buildspec-prod.yml | 26 ++++++++------------------ ServerlessFunction/buildspec-test.yml | 23 ++++++++--------------- 2 files changed, 16 insertions(+), 33 deletions(-) diff --git a/ServerlessFunction/buildspec-prod.yml b/ServerlessFunction/buildspec-prod.yml index 6b8ed3e3..25d01611 100644 --- a/ServerlessFunction/buildspec-prod.yml +++ b/ServerlessFunction/buildspec-prod.yml @@ -5,7 +5,6 @@ env: SAM_CLI_TELEMETRY: 0 GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.parallel=true -Dorg.gradle.caching=true" ENVIRONMENT: prod - STACK_NAME: group2-englishstudy-prod phases: install: @@ -28,26 +27,17 @@ phases: - echo "Building SAM application for $ENVIRONMENT..." - cd $CODEBUILD_SRC_DIR/ServerlessFunction - sam build --parallel --cached - - echo "Build completed" + - echo "Packaging SAM application..." + - sam package --s3-bucket group2-englishstudy-pipeline-artifacts --s3-prefix sam-packages/$ENVIRONMENT --output-template-file packaged-template.yaml post_build: commands: - - echo "Deploying to $ENVIRONMENT environment..." - - cd $CODEBUILD_SRC_DIR/ServerlessFunction - - | - sam deploy \ - --stack-name $STACK_NAME \ - --s3-bucket group2-englishstudy-pipeline-artifacts \ - --s3-prefix sam-deploy/prod \ - --region ap-northeast-2 \ - --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \ - --no-confirm-changeset \ - --no-fail-on-empty-changeset \ - --parameter-overrides \ - Environment=$ENVIRONMENT \ - ExistingCognitoUserPoolId=ap-northeast-2_ezDwzFCzR \ - ExistingCognitoClientId=4ns077jcr1pkue2vvisr6qdpu5 - - echo "Deployment completed on $(date)" + - echo "Build completed on $(date)" + +artifacts: + files: + - packaged-template.yaml + base-directory: ServerlessFunction cache: paths: diff --git a/ServerlessFunction/buildspec-test.yml b/ServerlessFunction/buildspec-test.yml index e00db1d6..6d75e4f0 100644 --- a/ServerlessFunction/buildspec-test.yml +++ b/ServerlessFunction/buildspec-test.yml @@ -5,7 +5,6 @@ env: SAM_CLI_TELEMETRY: 0 GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.parallel=true -Dorg.gradle.caching=true" ENVIRONMENT: test - STACK_NAME: group2-englishstudy-test phases: install: @@ -28,23 +27,17 @@ phases: - echo "Building SAM application for $ENVIRONMENT..." - cd $CODEBUILD_SRC_DIR/ServerlessFunction - sam build --parallel --cached - - echo "Build completed" + - echo "Packaging SAM application..." + - sam package --s3-bucket group2-englishstudy-pipeline-artifacts --s3-prefix sam-packages/$ENVIRONMENT --output-template-file packaged-template.yaml post_build: commands: - - echo "Deploying to $ENVIRONMENT environment..." - - cd $CODEBUILD_SRC_DIR/ServerlessFunction - - | - sam deploy \ - --stack-name $STACK_NAME \ - --resolve-s3 \ - --s3-prefix $STACK_NAME \ - --region ap-northeast-2 \ - --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \ - --no-confirm-changeset \ - --no-fail-on-empty-changeset \ - --parameter-overrides Environment=$ENVIRONMENT ExistingCognitoUserPoolId=ap-northeast-2_ezDwzFCzR ExistingCognitoClientId=4ns077jcr1pkue2vvisr6qdpu5 - - echo "Deployment completed on $(date)" + - echo "Build completed on $(date)" + +artifacts: + files: + - packaged-template.yaml + base-directory: ServerlessFunction cache: paths: From cfc417500f0e90f3f6eb1eaa0780aedc873920aa Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Sun, 25 Jan 2026 18:48:54 +0900 Subject: [PATCH 08/16] fix: force Authorizer recreation to update Cognito User Pool --- ServerlessFunction/template.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index 225dc206..ab3187ed 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -147,6 +147,7 @@ Resources: Authorizers: CognitoAuthorizer: UserPoolArn: !Sub "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${ExistingCognitoUserPoolId}" + AuthorizationScopes: [] Identity: Header: Authorization From 81ab3cbb4cb1325d31bd2532266ab1ca15e4d5a8 Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Sun, 25 Jan 2026 19:13:00 +0900 Subject: [PATCH 09/16] fix: rename Authorizer to CognitoAuthV2 to force recreation with correct User Pool --- ServerlessFunction/template.yaml | 133 +++++++++++++++---------------- 1 file changed, 66 insertions(+), 67 deletions(-) diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index ab3187ed..ed741aeb 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -142,12 +142,11 @@ Resources: ResponseTemplates: application/json: '{"message": "Token expired", "statusCode": 401}' Auth: - DefaultAuthorizer: CognitoAuthorizer + DefaultAuthorizer: CognitoAuthV2 AddDefaultAuthorizerToCorsPreflight: false Authorizers: - CognitoAuthorizer: + CognitoAuthV2: UserPoolArn: !Sub "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${ExistingCognitoUserPoolId}" - AuthorizationScopes: [] Identity: Header: Authorization @@ -403,7 +402,7 @@ Resources: Path: /chat/rooms Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetRooms: Type: Api Properties: @@ -411,7 +410,7 @@ Resources: Path: /chat/rooms Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetRoom: Type: Api Properties: @@ -419,7 +418,7 @@ Resources: Path: /chat/rooms/{roomId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DeleteRoom: Type: Api Properties: @@ -427,7 +426,7 @@ Resources: Path: /chat/rooms/{roomId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 JoinRoom: Type: Api Properties: @@ -435,7 +434,7 @@ Resources: Path: /chat/rooms/{roomId}/join Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 LeaveRoom: Type: Api Properties: @@ -443,7 +442,7 @@ Resources: Path: /chat/rooms/{roomId}/leave Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GameFunction: Type: AWS::Serverless::Function @@ -490,7 +489,7 @@ Resources: Path: /chat/rooms/{roomId}/game/start Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 StopGame: Type: Api Properties: @@ -498,7 +497,7 @@ Resources: Path: /chat/rooms/{roomId}/game/stop Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetGameStatus: Type: Api Properties: @@ -506,7 +505,7 @@ Resources: Path: /chat/rooms/{roomId}/game/status Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetScores: Type: Api Properties: @@ -514,7 +513,7 @@ Resources: Path: /chat/rooms/{roomId}/game/scores Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 끝말잇기(Word Chain) 게임 핸들러 WordChainFunction: @@ -547,7 +546,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/start Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 SubmitWord: Type: Api Properties: @@ -555,7 +554,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/submit Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 HandleTimeout: Type: Api Properties: @@ -563,7 +562,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/timeout Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 StopWordChain: Type: Api Properties: @@ -571,7 +570,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/stop Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetWordChainStatus: Type: Api Properties: @@ -579,7 +578,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/status Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 게임 자동 종료 Lambda (EventBridge Scheduler에 의해 호출) GameAutoCloseFunction: @@ -667,7 +666,7 @@ Resources: Path: /chat/rooms/{roomId}/messages Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetMessages: Type: Api Properties: @@ -675,7 +674,7 @@ Resources: Path: /chat/rooms/{roomId}/messages Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetMessage: Type: Api Properties: @@ -683,7 +682,7 @@ Resources: Path: /chat/rooms/{roomId}/messages/{messageId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ChatVoiceFunction: Type: AWS::Serverless::Function @@ -713,7 +712,7 @@ Resources: Path: /chat/voice/synthesize Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Vocabulary Lambda Functions @@ -817,7 +816,7 @@ Resources: Path: /vocab/wrong-answers Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetUserWords: Type: Api Properties: @@ -825,7 +824,7 @@ Resources: Path: /vocab/user-words Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetUserWord: Type: Api Properties: @@ -833,7 +832,7 @@ Resources: Path: /vocab/user-words/{wordId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateUserWord: Type: Api Properties: @@ -841,7 +840,7 @@ Resources: Path: /vocab/user-words/{wordId} Method: PUT Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateUserWordTag: Type: Api Properties: @@ -849,7 +848,7 @@ Resources: Path: /vocab/user-words/{wordId}/tag Method: PATCH Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateUserWordStatus: Type: Api Properties: @@ -857,7 +856,7 @@ Resources: Path: /vocab/user-words/{wordId}/status Method: PATCH Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 WordGroupFunction: Type: AWS::Serverless::Function @@ -879,7 +878,7 @@ Resources: Path: /vocab/groups Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetGroups: Type: Api Properties: @@ -887,7 +886,7 @@ Resources: Path: /vocab/groups Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetGroupDetail: Type: Api Properties: @@ -895,7 +894,7 @@ Resources: Path: /vocab/groups/{groupId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateGroup: Type: Api Properties: @@ -903,7 +902,7 @@ Resources: Path: /vocab/groups/{groupId} Method: PUT Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DeleteGroup: Type: Api Properties: @@ -911,7 +910,7 @@ Resources: Path: /vocab/groups/{groupId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 AddWordToGroup: Type: Api Properties: @@ -919,7 +918,7 @@ Resources: Path: /vocab/groups/{groupId}/words/{wordId} Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 RemoveWordFromGroup: Type: Api Properties: @@ -927,7 +926,7 @@ Resources: Path: /vocab/groups/{groupId}/words/{wordId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DailyStudyFunction: Type: AWS::Serverless::Function @@ -954,7 +953,7 @@ Resources: Path: /vocab/daily Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 MarkWordLearned: Type: Api Properties: @@ -962,7 +961,7 @@ Resources: Path: /vocab/daily/words/{wordId}/learned Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 TestFunction: Type: AWS::Serverless::Function @@ -992,7 +991,7 @@ Resources: Path: /vocab/test/start Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 SubmitAnswer: Type: Api Properties: @@ -1000,7 +999,7 @@ Resources: Path: /vocab/test/submit Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTestResults: Type: Api Properties: @@ -1008,7 +1007,7 @@ Resources: Path: /vocab/test/results Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTestResultDetail: Type: Api Properties: @@ -1016,7 +1015,7 @@ Resources: Path: /vocab/test/results/{testId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTestedWords: Type: Api Properties: @@ -1024,7 +1023,7 @@ Resources: Path: /vocab/test/tested-words Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 StatsFunction: Type: AWS::Serverless::Function @@ -1046,7 +1045,7 @@ Resources: Path: /vocab/stats Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetDailyStats: Type: Api Properties: @@ -1054,7 +1053,7 @@ Resources: Path: /vocab/stats/daily Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetWeaknessAnalysis: Type: Api Properties: @@ -1062,7 +1061,7 @@ Resources: Path: /vocab/stats/weakness Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 VocabVoiceFunction: Type: AWS::Serverless::Function @@ -1144,7 +1143,7 @@ Resources: Path: /stats/daily Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetWeeklyStats: Type: Api Properties: @@ -1152,7 +1151,7 @@ Resources: Path: /stats/weekly Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetMonthlyStats: Type: Api Properties: @@ -1160,7 +1159,7 @@ Resources: Path: /stats/monthly Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTotalStats: Type: Api Properties: @@ -1168,7 +1167,7 @@ Resources: Path: /stats/total Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetDashboard: Type: Api Properties: @@ -1176,7 +1175,7 @@ Resources: Path: /stats/dashboard Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetStatsHistory: Type: Api Properties: @@ -1184,7 +1183,7 @@ Resources: Path: /stats/history Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # Badge Lambda Function BadgeFunction: @@ -1214,7 +1213,7 @@ Resources: Path: /badges Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetEarnedBadges: Type: Api Properties: @@ -1222,7 +1221,7 @@ Resources: Path: /badges/earned Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Grammar Lambda Functions @@ -1269,7 +1268,7 @@ Resources: Path: /grammar/check Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GrammarConversation: Type: Api Properties: @@ -1277,7 +1276,7 @@ Resources: Path: /grammar/conversation Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetSessions: Type: Api Properties: @@ -1285,7 +1284,7 @@ Resources: Path: /grammar/sessions Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetSessionDetail: Type: Api Properties: @@ -1293,7 +1292,7 @@ Resources: Path: /grammar/sessions/{sessionId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DeleteSession: Type: Api Properties: @@ -1301,7 +1300,7 @@ Resources: Path: /grammar/sessions/{sessionId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Grammar WebSocket API (Streaming) @@ -1565,7 +1564,7 @@ Resources: Path: /opic/sessions Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 세션 조회 GetSession: Type: Api @@ -1574,7 +1573,7 @@ Resources: Path: /opic/sessions/{sessionId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 세션 목록 조회 GetSessions: Type: Api @@ -1583,7 +1582,7 @@ Resources: Path: /opic/sessions Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 다음 질문 조회 GetNextQuestion: Type: Api @@ -1592,7 +1591,7 @@ Resources: Path: /opic/sessions/{sessionId}/questions/next Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 답변 제출 SubmitAnswer: Type: Api @@ -1601,7 +1600,7 @@ Resources: Path: /opic/sessions/{sessionId}/answers Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 세션 완료 CompleteSession: Type: Api @@ -1610,7 +1609,7 @@ Resources: Path: /opic/sessions/{sessionId}/complete Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 음성 업로드 Presigned URL GetUploadUrl: Type: Api @@ -1619,7 +1618,7 @@ Resources: Path: /opic/sessions/{sessionId}/upload-url Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Speaking Lambda Functions @@ -1661,7 +1660,7 @@ Resources: Path: /speaking/chat Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 SpeakingReset: Type: Api Properties: @@ -1669,7 +1668,7 @@ Resources: Path: /speaking/reset Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # DynamoDB Tables From c03c3c9bedbb93102c6c4c89fa52dd99d06375a3 Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Sun, 25 Jan 2026 19:23:39 +0900 Subject: [PATCH 10/16] fix: use GSI1 index for WordChainSession roomId queries findActiveByRoomId was querying main table with ROOM#roomId partition key, but the main table PK is WORDCHAIN#sessionId. Fixed to use GSI1 index which has ROOM#roomId as partition key. --- .../repository/WordChainSessionRepository.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java index ca39ddf7..b871987f 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java @@ -5,6 +5,7 @@ import com.mzc.secondproject.serverless.domain.chatting.model.WordChainSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbIndex; import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; import software.amazon.awssdk.enhanced.dynamodb.Key; import software.amazon.awssdk.enhanced.dynamodb.TableSchema; @@ -19,16 +20,20 @@ public class WordChainSessionRepository { private static final Logger logger = LoggerFactory.getLogger(WordChainSessionRepository.class); private static final String TABLE_NAME = EnvConfig.getRequired("CHAT_TABLE_NAME"); + private static final String GSI1_INDEX_NAME = "GSI1"; private final DynamoDbTable table; + private final DynamoDbIndex gsi1Index; public WordChainSessionRepository() { this.table = AwsClients.dynamoDbEnhanced() .table(TABLE_NAME, TableSchema.fromBean(WordChainSession.class)); + this.gsi1Index = table.index(GSI1_INDEX_NAME); } public WordChainSessionRepository(DynamoDbTable table) { this.table = table; + this.gsi1Index = table.index(GSI1_INDEX_NAME); } /** @@ -52,16 +57,16 @@ public Optional findById(String sessionId) { } /** - * 방의 활성 세션 조회 + * 방의 활성 세션 조회 (GSI1 인덱스 사용) */ public Optional findActiveByRoomId(String roomId) { - return table.query(QueryConditional.sortBeginsWith( + return gsi1Index.query(QueryConditional.sortBeginsWith( Key.builder() .partitionValue("ROOM#" + roomId) .sortValue("WORDCHAIN#") .build())) - .items() .stream() + .flatMap(page -> page.items().stream()) .filter(WordChainSession::isActive) .findFirst(); } From 51f27a775f9a17df694d41aaee8fb5c7f5015399 Mon Sep 17 00:00:00 2001 From: hyein Heo <128613248+hye-inA@users.noreply.github.com> Date: Mon, 26 Jan 2026 00:42:07 +0900 Subject: [PATCH 11/16] =?UTF-8?q?feature=20:=20Cognito=20Authorizer(?= =?UTF-8?q?=ED=86=A0=ED=81=B0)=EC=97=90=EC=84=9C=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=EC=9D=84=20=EC=B6=94=EC=B6=9C=20+=20DB=EC=97=90=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20(#543)=20(#544)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - … Co-authored-by: ddingjoo Co-authored-by: Claude Opus 4.5 From 68d2d43555ccebb308a17a817231401e4386a976 Mon Sep 17 00:00:00 2001 From: hyein Heo <128613248+hye-inA@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:33:19 +0900 Subject: [PATCH 12/16] =?UTF-8?q?feature=20:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=EC=97=90=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EB=8F=99=EA=B8=B0=ED=99=94=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80=20=20(#546)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature : Cognito Authorizer(토큰)에서 닉네임을 추출 + DB에 저장 (#543) * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage Co-Authored-By: Claude Opus 4.5 * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments Co-Authored-By: Claude Opus 4.5 * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility Co-Authored-By: Claude Opus 4.5 * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL Co-Authored-By: Claude Opus 4.5 * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests Co-Authored-By: Claude Opus 4.5 * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records Co-Authored-By: Claude Opus 4.5 * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - … * feature : 채팅메세지에 nickname 필드 추가 (#545) * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage Co-Authored-By: Claude Opus 4.5 * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments Co-Authored-By: Claude Opus 4.5 * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility Co-Authored-By: Claude Opus 4.5 * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL Co-Authored-By: Claude Opus 4.5 * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests Co-Authored-By: Claude Opus 4.5 * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records Co-Authored-By: Claude Opus 4.5 * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT … --------- Co-authored-by: ddingjoo Co-authored-by: Claude Opus 4.5 --- .../chatting/handler/ChatMessageHandler.java | 22 ++++++++++- .../websocket/WebSocketConnectHandler.java | 38 ++++++++++++++++++- .../domain/chatting/model/Connection.java | 1 + 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/ChatMessageHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/ChatMessageHandler.java index b156feba..07ef6ac9 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/ChatMessageHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/ChatMessageHandler.java @@ -14,6 +14,8 @@ import com.mzc.secondproject.serverless.domain.chatting.model.ChatMessage; import com.mzc.secondproject.serverless.domain.chatting.repository.ChatRoomRepository; import com.mzc.secondproject.serverless.domain.chatting.service.ChatMessageService; +import com.mzc.secondproject.serverless.domain.user.model.User; +import com.mzc.secondproject.serverless.domain.user.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,21 +31,23 @@ public class ChatMessageHandler implements RequestHandler handleRequest(Map event, Context cont RoomToken token = optToken.get(); String userId = token.getUserId(); String roomId = token.getRoomId(); - + String nickname = "Unknown"; + + // Cognito Authorizer에서 닉네임 추출 + try { + if (event.containsKey("requestContext")) { + Map reqCtx = (Map) event.get("requestContext"); + + if (reqCtx.containsKey("authorizer")) { + Map auth = (Map) reqCtx.get("authorizer"); + + Map claims = auth; + if (auth.containsKey("claims")) { + claims = (Map) auth.get("claims"); + } else if (auth.containsKey("principalId")) { + claims = auth; + } + + if (claims != null) { + if (claims.get("nickname") != null) { + nickname = (String) claims.get("nickname"); + } else if (claims.get("custom:nickname") != null) { + nickname = (String) claims.get("custom:nickname"); + } else if (claims.get("name") != null) { + nickname = (String) claims.get("name"); + } + } + } + } + // 닉네임 못찾았으면 UserId 앞부분 표시 + if ("Unknown".equals(nickname) && userId != null && userId.length() > 5) { + nickname = "User-" + userId.substring(0, 5); + } + } catch (Exception ex) { + logger.warn("닉네임 표시 실패: {}", ex.getMessage()); + } + // 같은 방에서 기존 연결 삭제 (새로고침 시 중복 연결 방지) connectionRepository.deleteUserConnectionsInRoom(userId, roomId); @@ -72,6 +107,7 @@ public Map handleRequest(Map event, Context cont .gsi2sk("CONN#" + connectionId) .connectionId(connectionId) .userId(userId) + .nickname(nickname) .roomId(roomId) .connectedAt(now) .ttl(ttl) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/Connection.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/Connection.java index 7c130390..5aa02e20 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/Connection.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/Connection.java @@ -22,6 +22,7 @@ public class Connection { private String connectionId; private String userId; + private String nickname; private String roomId; private String connectedAt; private Long ttl; // 10분 후 자동 삭제 From e8909f213c872d51114de756fb129328e6cdd728 Mon Sep 17 00:00:00 2001 From: hyein Heo <128613248+hye-inA@users.noreply.github.com> Date: Mon, 26 Jan 2026 11:03:03 +0900 Subject: [PATCH 13/16] =?UTF-8?q?feature=20:=20=EB=B8=8C=EB=A1=9C=EB=93=9C?= =?UTF-8?q?=EC=BA=90=EC=8A=A4=ED=8A=B8=20=EB=A9=94=EC=8B=9C=EC=A7=80?= =?UTF-8?q?=EC=97=90=20nickname=20=EC=B6=94=EA=B0=80=20(#548)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature : Cognito Authorizer(토큰)에서 닉네임을 추출 + DB에 저장 (#543) * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage Co-Authored-By: Claude Opus 4.5 * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments Co-Authored-By: Claude Opus 4.5 * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility Co-Authored-By: Claude Opus 4.5 * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL Co-Authored-By: Claude Opus 4.5 * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests Co-Authored-By: Claude Opus 4.5 * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records Co-Authored-By: Claude Opus 4.5 * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - … * feature : 채팅메세지에 nickname 필드 추가 (#545) * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage Co-Authored-By: Claude Opus 4.5 * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments Co-Authored-By: Claude Opus 4.5 * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility Co-Authored-By: Claude Opus 4.5 * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL Co-Authored-By: Claude Opus 4.5 * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests Co-Authored-By: Claude Opus 4.5 * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records Co-Authored-By: Claude Opus 4.5 * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT … * featrue : 브로드캐스트 메시지에 nickname 추가 (#547) * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage Co-Authored-By: Claude Opus 4.5 * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments Co-Authored-By: Claude Opus 4.5 * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility Co-Authored-By: Claude Opus 4.5 * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL Co-Authored-By: Claude Opus 4.5 * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests Co-Authored-By: Claude Opus 4.5 * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records Co-Authored-By: Claude Opus 4.5 * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한… --------- Co-authored-by: ddingjoo Co-authored-by: Claude Opus 4.5 --- .../chatting/handler/websocket/WebSocketMessageHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java index 0435d5cb..c490e093 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java @@ -202,6 +202,7 @@ private Map handleRegularMessage(String connectionId, MessagePay broadcastMessage.put("messageId", savedMessage.getMessageId()); broadcastMessage.put("roomId", savedMessage.getRoomId()); broadcastMessage.put("userId", savedMessage.getUserId()); + broadcastMessage.put("nickname", savedMessage.getNickname()); broadcastMessage.put("content", savedMessage.getContent()); broadcastMessage.put("messageType", savedMessage.getMessageType()); broadcastMessage.put("createdAt", savedMessage.getCreatedAt()); From e5e043bbc1a48268064186b814637a6b73e43244 Mon Sep 17 00:00:00 2001 From: hyein Heo <128613248+hye-inA@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:46:25 +0900 Subject: [PATCH 14/16] =?UTF-8?q?refactor=20:=20OPIc=20=EC=A7=88=EB=AC=B8?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=A7=88=EB=AC=B8=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80=20(#551)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature : Cognito Authorizer(토큰)에서 닉네임을 추출 + DB에 저장 (#543) * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage Co-Authored-By: Claude Opus 4.5 * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments Co-Authored-By: Claude Opus 4.5 * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility Co-Authored-By: Claude Opus 4.5 * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL Co-Authored-By: Claude Opus 4.5 * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests Co-Authored-By: Claude Opus 4.5 * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records Co-Authored-By: Claude Opus 4.5 * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - … * feature : 채팅메세지에 nickname 필드 추가 (#545) * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage Co-Authored-By: Claude Opus 4.5 * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments Co-Authored-By: Claude Opus 4.5 * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility Co-Authored-By: Claude Opus 4.5 * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL Co-Authored-By: Claude Opus 4.5 * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests Co-Authored-By: Claude Opus 4.5 * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records Co-Authored-By: Claude Opus 4.5 * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT … * featrue : 브로드캐스트 메시지에 nickname 추가 (#547) * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage Co-Authored-By: Claude Opus 4.5 * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments Co-Authored-By: Claude Opus 4.5 * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility Co-Authored-By: Claude Opus 4.5 * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL Co-Authored-By: Claude Opus 4.5 * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests Co-Authored-By: Claude Opus 4.5 * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records Co-Authored-By: Claude Opus 4.5 * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한… * refactor : OPIc 질문 조회 로직 구조 변경 및 질문 데이터 추가 (#550) * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- Co-authored-by: hyein Heo <128613248+hye-inA@users.noreply.github.com> * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage Co-Authored-By: Claude Opus 4.5 * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments Co-Authored-By: Claude Opus 4.5 * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility Co-Authored-By: Claude Opus 4.5 * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL Co-Authored-By: Claude Opus 4.5 * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests Co-Authored-By: Claude Opus 4.5 * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- Co-authored-by: ddingjoo * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- Co-authored-by: DDING JOO * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- Co-authored-by: DDING JOO * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- Co-authored-by: DDING JOO * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records Co-Authored-By: Claude Opus 4.5 * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사… --------- Co-authored-by: ddingjoo Co-authored-by: Claude Opus 4.5 --- .../opic/handler/OPIcSessionHandler.java | 76 ++++++------ .../domain/opic/model/OPIcQuestion.java | 8 +- .../opic/repository/OPIcRepository.java | 30 ++--- seed/opic/question-homes.json | 108 ------------------ 4 files changed, 56 insertions(+), 166 deletions(-) delete mode 100644 seed/opic/question-homes.json diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java index 0eb3b1a2..cd7335d4 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java @@ -11,7 +11,9 @@ import com.mzc.secondproject.serverless.common.util.ResponseGenerator; import com.mzc.secondproject.serverless.domain.opic.dto.request.CreateSessionRequest; import com.mzc.secondproject.serverless.domain.opic.dto.request.SubmitAnswerRequest; +import com.mzc.secondproject.serverless.domain.opic.dto.response.CreateSessionResponse; import com.mzc.secondproject.serverless.domain.opic.dto.response.FeedbackResponse; +import com.mzc.secondproject.serverless.domain.opic.dto.response.QuestionResponse; import com.mzc.secondproject.serverless.domain.opic.model.OPIcAnswer; import com.mzc.secondproject.serverless.domain.opic.model.OPIcQuestion; import com.mzc.secondproject.serverless.domain.opic.model.OPIcSession; @@ -119,55 +121,59 @@ public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent ev */ private APIGatewayProxyResponseEvent createSession(APIGatewayProxyRequestEvent event, String userId) { CreateSessionRequest request = gson.fromJson(event.getBody(), CreateSessionRequest.class); - - logger.info("세션 생성 요청: userId={}, topic={}, level={}", - userId, request.topic(), request.targetLevel()); - - // 주제 + 소주제 + 레벨로 질문 세트 조회 - List questions = repository.findQuestionsByTopicSubTopicAndLevel( - request.topic(), - request.subTopic(), - request.targetLevel() + + logger.info("세션 생성 요청: userId={}, topic={}, subTopic={}, targetLevel={}", + userId, request.topic(), request.subTopic(), request.targetLevel()); + + // 질문 세트 조회 (주제+소주제로 조회) + List questions = repository.findQuestionsByTopicAndSubTopic( + request.topic(), // 예: "DESCRIPTION" + request.subTopic() // 예: "HOMES" ); - + + // 질문 데이터 없음 예외 처리 if (questions.isEmpty()) { - return ResponseGenerator.notFound("해당 주제/레벨의 질문이 없습니다."); + String msg = String.format("해당 주제(%s)와 소주제(%s)에 해당하는 질문이 없습니다.", + request.topic(), request.subTopic()); + return ResponseGenerator.notFound(msg); } - - // 최대 3개 질문 선택 (랜덤 셔플) + + // 질문 3개 랜덤 선택 Collections.shuffle(questions); - List questionIds = questions.stream() + List selectedQuestions = questions.stream() .limit(3) + .sorted(Comparator.comparingInt(OPIcQuestion::getOrderInSet)) + .collect(Collectors.toList()); + + // 질문 ID 목록 추출 + List questionIds = selectedQuestions.stream() .map(OPIcQuestion::getQuestionId) .collect(Collectors.toList()); - - // 세션 생성 + + // 세션 저장 OPIcSession session = repository.createSession( userId, request.topic(), request.subTopic(), - request.targetLevel(), + request.targetLevel(), // 사용자가 선택한 레벨 저장 (AL, IM2 등) questionIds ); - - // 첫 질문 Polly 음성 URL 생성 (#368 PollyService 연동) - OPIcQuestion firstQuestion = questions.get(0); + + // 첫 번째 질문 응답 생성 + OPIcQuestion firstQuestion = selectedQuestions.get(0); String audioUrl = generateQuestionAudioUrl(firstQuestion); - - // Response - Map response = new LinkedHashMap<>(); - response.put("sessionId", session.getSessionId()); - response.put("totalQuestions", session.getTotalQuestions()); - response.put("firstQuestion", Map.of( - "questionId", firstQuestion.getQuestionId(), - "questionText", firstQuestion.getQuestionText(), - "audioUrl", audioUrl, - "questionNumber", 1, - "totalQuestions", session.getTotalQuestions() - )); - - logger.info("세션 생성 완료: sessionId={}", session.getSessionId()); - return ResponseGenerator.created("세션이 생성되었습니다.", response); + + QuestionResponse questionResponse = new QuestionResponse( + firstQuestion.getQuestionId(), + firstQuestion.getQuestionText(), + audioUrl, + 1, // 현재 질문 번호 + 3 // 총 질문 수 + ); + + return ResponseGenerator.ok( + new CreateSessionResponse(session.getSessionId(), questionResponse, 3) + ); } /** diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/model/OPIcQuestion.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/model/OPIcQuestion.java index 6a2056b1..0a85b9d4 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/model/OPIcQuestion.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/model/OPIcQuestion.java @@ -10,12 +10,12 @@ public class OPIcQuestion { private String pk; // QUESTION#questionId private String sk; // METADATA - private String gsi1pk; // TOPIC#travel - private String gsi1sk; // LEVEL#IM2 + private String gsi1pk; // TOPIC#DESCRIPTION (질문 유형 - 대주제) + private String gsi1sk; // SUBTOPIC#HOMES (질문 소재 - 소주제) private String questionId; - private String topic; // 대주제 - private String subTopic; // 소주제 + private String topic; // DESCRIPTION, HABIT, PAST_EXPERIENCE ... + private String subTopic; // HOMES, BANKS, MUSIC ... private String level; // 난이도 (IM1, IM2, IM3, IH, AL) private String questionText; // 질문 텍스트 (영어) private String questionTextKo; // 질문 텍스트 (한국어, 참고용) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/repository/OPIcRepository.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/repository/OPIcRepository.java index 62251d18..3b53f323 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/repository/OPIcRepository.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/repository/OPIcRepository.java @@ -151,20 +151,23 @@ public Optional findQuestionById(String questionId) { return Optional.ofNullable(questionTable.getItem(key)); } - + /** - * 주제 + 레벨로 질문 조회 (GSI1) + * 질문 유형 + 질문 주제로 조회 */ - public List findQuestionsByTopicAndLevel(String topic, String level) { + public List findQuestionsByTopicAndSubTopic(String topic, String subTopic) { DynamoDbIndex gsi1 = questionTable.index("GSI1"); - - QueryConditional queryConditional = QueryConditional.keyEqualTo( + + String pkVal = "TOPIC#" + topic.toUpperCase(); + String skVal = "SUBTOPIC#" + subTopic.toUpperCase(); + + QueryConditional queryConditional = QueryConditional.sortBeginsWith( Key.builder() - .partitionValue("TOPIC#" + topic) - .sortValue("LEVEL#" + level) + .partitionValue(pkVal) + .sortValue(skVal) .build() ); - + return gsi1.query(queryConditional) .stream() .flatMap(page -> page.items().stream()) @@ -172,17 +175,6 @@ public List findQuestionsByTopicAndLevel(String topic, String leve .collect(Collectors.toList()); } - /** - * 주제 + 소주제 + 레벨로 질문 조회 (subTopic 필터 추가) - */ - public List findQuestionsByTopicSubTopicAndLevel( - String topic, String subTopic, String level) { - - return findQuestionsByTopicAndLevel(topic, level).stream() - .filter(q -> subTopic == null || subTopic.equals(q.getSubTopic())) - .collect(Collectors.toList()); - } - /** * 여러 질문 ID로 조회 */ diff --git a/seed/opic/question-homes.json b/seed/opic/question-homes.json deleted file mode 100644 index ddd67703..00000000 --- a/seed/opic/question-homes.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "questions": [ - { - "questionId": "desc-homes-001", - "topic": "DESCRIPTION", - "subTopic": "HOMES", - "questionText": "I would like to know about where you live. Talk about the different rooms at your place. Tell me about your favorite room in your home. What does it look like?", - "difficulty": "IM2", - "questionNumber": 44 - }, - { - "questionId": "desc-homes-002", - "topic": "DESCRIPTION", - "subTopic": "HOMES", - "questionText": "I would like to know where you live. Can you describe your home to me? What does it look like? How many rooms does it have? Give me a description with lots of details.", - "difficulty": "IM2", - "questionNumber": 45 - }, - { - "questionId": "desc-homes-003", - "topic": "DESCRIPTION", - "subTopic": "HOMES", - "questionText": "Now, let's talk about your bedroom. What's inside? What kind of furniture do you have in your room?", - "difficulty": "IM1", - "questionNumber": 46 - }, - { - "questionId": "habit-homes-001", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "What kinds of home improvement projects do you enjoy doing and why?", - "difficulty": "IM2", - "questionNumber": 139 - }, - { - "questionId": "habit-homes-002", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "Tell me about your normal routine at home. What do you do on weekdays and on weekends?", - "difficulty": "IM1", - "questionNumber": 140 - }, - { - "questionId": "habit-homes-003", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "What is your normal routine at home? What things do you usually do on weekdays and on weekends?", - "difficulty": "IM1", - "questionNumber": 141 - }, - { - "questionId": "habit-homes-004", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "What is your responsibility at home? What is your role? Tell me in detail.", - "difficulty": "IM2", - "questionNumber": 142 - }, - { - "questionId": "habit-homes-005", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "What kinds of things do you do to keep your house clean and comfortable? What kinds of housework do you do at home?", - "difficulty": "IM1", - "questionNumber": 143 - }, - { - "questionId": "habit-homes-006", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "What kind of house work do you usually do at home? Do you share your chores with your family?", - "difficulty": "IM1", - "questionNumber": 144 - }, - { - "questionId": "habit-homes-007", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "Tell me about all the steps you take for a home improvement project. What steps do you usually take? How do you complete the project?", - "difficulty": "IH", - "questionNumber": 145 - }, - { - "questionId": "past-homes-001", - "topic": "PAST_EXPERIENCE", - "subTopic": "HOMES", - "questionText": "Tell me about a memorable experience you had at home. What happened and why was it memorable?", - "difficulty": "IM2", - "questionNumber": 0 - }, - { - "questionId": "past-homes-002", - "topic": "PAST_EXPERIENCE", - "subTopic": "HOMES", - "questionText": "Have you ever had any problems at home? Maybe something broke or there was an issue with your neighbors. Tell me about that experience and how you solved it.", - "difficulty": "IH", - "questionNumber": 0 - }, - { - "questionId": "past-homes-003", - "topic": "PAST_EXPERIENCE", - "subTopic": "HOMES", - "questionText": "Tell me about a time when you had to do a major cleaning or organizing at home. What did you do and how did it turn out?", - "difficulty": "IM2", - "questionNumber": 0 - } - ] -} From f5032722a00799d37b12ee0d244633c4baeaa856 Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Tue, 27 Jan 2026 12:12:34 +0900 Subject: [PATCH 15/16] feat: Enable SnapStart with AutoPublishAlias for all Lambda functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add AutoPublishAlias: live to Globals.Function section - This enables SnapStart optimization by creating published versions - Expected Cold Start improvement: 3,500ms → 200ms (94% reduction) --- ServerlessFunction/template.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index ed741aeb..385d914e 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -30,6 +30,7 @@ Globals: Architectures: - x86_64 Tracing: Active + AutoPublishAlias: live Environment: Variables: USER_TABLE_NAME: !Ref UserTable From bbff1cf2a475904e7f668765159c862aa9f72601 Mon Sep 17 00:00:00 2001 From: hyein Heo <128613248+hye-inA@users.noreply.github.com> Date: Tue, 27 Jan 2026 12:28:30 +0900 Subject: [PATCH 16/16] =?UTF-8?q?feature=20:=20OPIc=20=EC=84=B8=EC=85=98?= =?UTF-8?q?=20=EC=99=84=EB=A3=8C=20=EC=B2=98=EB=A6=AC=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EB=A6=AC=ED=8F=AC=ED=8A=B8=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EB=B0=9C=EC=86=A1=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#552)=20(#554)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * Release: 캐치마인드 게임 분리, AI 회화 연습, CI/CD 파이프라인 (#469) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 ## 변경 사항 ### GSI1SK 포맷 변경 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} ### 지원 쿼리 패턴 - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") ### 파일 수정 - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) ### 마이그레이션 - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes --------- * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * feat(news): 뉴스 도메인 기반 구조 구축 (#385) - NewsCategory, QuizType enum 추가 - NewsKey 상수 클래스 추가 - NewsErrorCode 예외 클래스 추가 - NewsArticle, KeywordInfo, QuizQuestion 모델 추가 - NewsArticleRepository CRUD 구현 - NewsTable DynamoDB 테이블 정의 - CORS GatewayResponses 설정 추가 * feat(news): 뉴스 수집 파이프라인 구현 (#386) - NewsApiClient: NewsAPI 연동 서비스 - RssFeedParser: RSS 피드 파싱 (BBC, VOA, NPR) - NewsDuplicateChecker: URL 기반 중복 필터링 - NewsCollectorService: 수집 오케스트레이션 - NewsCollectionHandler: Lambda 핸들러 - EventBridge 스케줄러: 매일 18시 KST 실행 * refactor(news): NewsAPI 제거, RSS만 사용 - NewsApiClient 삭제 - SSM Parameter 의존성 제거 - RSS 소스당 7개씩 수집 (BBC, VOA, NPR = 약 21개/일) * feat(news): AI 뉴스 분석 시스템 구현 (#387) - NewsAnalysisService: AI 분석 통합 서비스 - Bedrock: CEFR 난이도 분석 (A1~C2) - Bedrock: 3줄 요약 + 퀴즈 3문제 생성 - Comprehend: 핵심 키워드 추출 - NewsCollectorService: 수집 시 자동 분석 연동 - GSI1/GSI2 키 자동 설정 (레벨별, 카테고리별 조회) * feat(news): 뉴스 학습 API 구현 (#388) - NewsQueryService: 뉴스 조회 서비스 - NewsHandler: API 핸들러 - GET /news - 목록 조회 (level, category 필터) - GET /news/today - 오늘의 뉴스 - GET /news/recommended - 내 레벨 맞춤 추천 - GET /news/{articleId} - 상세 조회 (조회수 증가) - template.yaml: NewsFunction Lambda 추가 * feat(news): 뉴스 학습 부가 기능 구현 (#389) - 읽기 완료 기록 API (POST /news/{articleId}/read) - 북마크 토글 API (POST /news/{articleId}/bookmark) - 북마크 목록 조회 API (GET /news/bookmarks) - 학습 통계 조회 API (GET /news/stats) - TTS 오디오 URL 조회 API (GET /news/{articleId}/audio) - UserNewsRecord 모델 추가 - UserNewsRepository 추가 - NewsLearningService 추가 * feat(news): 복합 퀴즈 시스템 구현 (#471) - 퀴즈 조회 API (GET /news/{articleId}/quiz) - 퀴즈 제출 API (POST /news/{articleId}/quiz) - 퀴즈 기록 조회 API (GET /news/quiz/history) - NewsQuizResult 모델 추가 - QuizAnswerResult 모델 추가 - NewsQuizRepository 추가 - NewsQuizService 추가 * feat(news): 단어 수집 & Vocabulary 연동 구현 (#472) - 단어 수집 API (POST /news/{articleId}/words) - 수집 단어 목록 API (GET /news/words) - 단어 상세 조회 API (GET /news/{articleId}/words/{word}) - 단어 삭제 API (DELETE /news/{articleId}/words/{word}) - Vocabulary 연동 API (POST /news/words/{word}/sync) - NewsWordCollect 모델 추가 - NewsWordRepository 추가 - NewsWordService 추가 * feat: add multi-environment deployment support (dev/test/prod) * fix: update buildspec.yml to deploy prod environment with parameter overrides * refactor : AI 말하기 Websocket 구현 -> REST API 구현으로 리팩토링 (#490) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: revert buildspec.yml to build-only for CloudFormation deploy stage * fix: update all API Gateway StageName to use Environment parameter * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: correct VocabularyTable and ContentBucket references in NewsFunction * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * fix: add stack name prefix to GameScheduleGroup and daily-stats schedule to avoid conflicts * feat: add support for existing Cognito User Pool reuse across environments * fix: add conditional Cognito ARN reference in API Gateway Authorizer * fix: remove Cognito resources completely, use existing Cognito only * fix: remove Cognito resources completely, use existing Cognito only * feat : speaking rest API 람다 함수 추가 * feat: add S3 bucket resource and fix environment-specific endpoints - Add ContentBucket S3 resource for content storage - Replace hardcoded /dev with ${Environment} in all WebSocket endpoints - Update Output URLs to use dynamic environment stage * fix: add stack name prefix to news-collection schedule name Prevent EventBridge rule name conflicts across environments * fix: add X-Requested-With and Accept headers to CORS config Enable additional headers for CloudFront CORS compatibility * fix: use environment variable for S3 bucket URLs Replace hardcoded bucket name with BUCKET_NAME env var for multi-env support: - PreSignUpHandler: dynamic default profile URL - PostConfirmationHandler: dynamic default profile URL - UserService: dynamic default profile URL - BadgeType: dynamic badge image base URL * fix: disable authorizer for CORS preflight requests Add AddDefaultAuthorizerToCorsPreflight: false to prevent Cognito Authorizer from blocking OPTIONS requests * feat : speaking REST API 람다 함수 추가 (#491) * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환경변수 추가 - WebSocketConnectFunction에 WEBSOCKET_ENDPOINT 추가 - WebSocketDisconnectFunction에 WEBSOCKET_ENDPOINT 추가 - execute-api:ManageConnections 권한 추가 * fix: Grammar WebSocket Lambda 환경 변수 및 권한 추가 - GrammarStreamingConnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - GrammarStreamingDisconnectFunction에 WEBSOCKET_ENDPOINT 환경 변수 추가 - 두 함수에 execute-api:ManageConnections 권한 추가 * feat: GSI1SK 확장성 있는 재설계 및 DB 레벨 필터링 - 기존: {level}#{createdAt} - 신규: {type}#{gameType}#{status}#{level}#{createdAt} - 전체 방: GSI1PK = "ROOMS" - 게임방만: begins_with(GSI1SK, "GAME#") - 캐치마인드만: begins_with(GSI1SK, "GAME#CATCHMIND#") - 대기중 캐치마인드: begins_with(GSI1SK, "GAME#CATCHMIND#WAITING#") - ChatRoomCommandService.java: 방 생성 시 새 GSI1SK 포맷 적용 - ChatRoomRepository.java: findByFilters() 메서드 추가, updateStatus() 메서드 추가 - ChatRoomQueryService.java: 메모리 필터링 제거, DB 레벨 필터링으로 변경 - GameService.java: 게임 시작/종료 시 방 상태 업데이트 (GSI1SK 포함) - scripts/migrate-gsi1sk.sh: 기존 데이터 마이그레이션 스크립트 - 37개 기존 방 마이그레이션 완료 * fix: increase stats query limit to 100 days - Adjust maximum allowable limit for recent stats query from 30 to 100 days to support extended data range responses. * feat: improve game round and connection management logic - Added handling for game round timeout (`ROUND_TIMEOUT`) in WebSocketMessageHandler. - Enhanced game start broadcast to include `currentWord` for the drawer only. - Updated ConnectionRepository to remove duplicate user connections in the same room. - Added `currentWordEnglish` to GameSession for better answer verification. - Normalized ChatRoom levels to match database storage format. - Updated template.yaml to include DynamoDBReadPolicy for VocabTable. * refactor: 코드 정리 및 미사용 클래스 제거 (#459) * refactor: remove unused WebSocketResponseUtil * refactor: add AutoCloseable to WebSocketBroadcaster * refactor: remove unused TestService * refactor: remove unused WordService * refactor: remove unused DailyStudyService * refactor: remove unused UserWordService * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * style: remove p tag from javadoc * fix: resolve N+1 query in StatsService * refactor(all): DI 패턴 및 전략 패턴 적용 (#461) - Repository, Service, Handler에 DI 생성자 추가 (테스트 용이성) - BadgeService에 Strategy 패턴 적용 (뱃지 조건 검증 로직 분리) * refactor: relocate and restructure seed data files - Moved `question-homes.json` and `words.json` from subdirectories to `seed` folder. - Updated file paths for better organization and clarity. * chore: seed 데이터 폴더 구조 정리 * feat: add CI/CD pipeline configuration for CodePipeline * fix: add SNS topic policy and DependsOn for notification rule * fix: correct paths in buildspec.yml for CodeBuild * fix: remove hardcoded JAVA_HOME, use runtime default * fix: add gradle wrapper for CI/CD build * fix: use single line sam package command with hardcoded bucket * fix: use existing stack name group2-englishstudy-chatting * fix: add missing WEBSOCKET_ENDPOINT env var to WebSocket connect functions * docs: update FRONTEND-API-GUIDE with new RoomType/RoomStatus structure * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: update WebSocketDisconnectHandler to use GameSession model - Remove references to deleted ChatRoom game fields - Use GameSessionRepository to finish active game sessions - Use ChatRoomRepository.updateStatus() for room state management * perf: optimize CI/CD build time - Add pip cache for SAM CLI dependencies - Enable Gradle parallel builds and build cache - Add SAM build cache (.aws-sam) - Use sam build --parallel --cached - Skip SAM CLI install if already cached - Remove unnecessary 'clean' to leverage cache * feat: add custom CodeBuild Docker image with pre-installed tools - Dockerfile with Java 21 + SAM CLI + Gradle pre-installed - build-and-push.sh script for ECR deployment - Updated buildspec.yml for custom image (removes SAM CLI install) - Expected build time reduction: ~30-40 seconds * feature : AI 영어 회화 연습 기능 (#468) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix: remove typo in SpeakingConnectionRepository * fix : 오타 수정 * chore: trigger build test with custom Docker image * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * chore: remove unnecessary newlines and whitespace across WebSocket handlers and related classes * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * fix: add CORS headers to API Gateway error responses (#479) API Gateway의 인증 실패 등 에러 응답에 CORS 헤더가 누락되어 CloudFront를 통한 프론트엔드 요청이 차단되는 문제 수정 - UNAUTHORIZED (401) 응답에 CORS 헤더 추가 - ACCESS_DENIED (403) 응답에 CORS 헤더 추가 - DEFAULT_4XX/5XX 응답에 CORS 헤더 추가 - EXPIRED_TOKEN 응답에 CORS 헤더 추가 * feat : speaking rest API 람다 함수 추가 --------- * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feature : test 벡엔드 서버에 AI 말하기 연습 기능 배포 (#492) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 --------- * feat : handleChat 메서드 JsonNull 체크 푸가 * feature : handleChat 메서드 JsonNull 체크 추가 (#493) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 --------- * feat(news): 뉴스 학습 배지 시스템 구현 (#473) - 14개 뉴스 관련 배지 추가 (읽기, 퀴즈, 단어수집, 연속학습, 마스터) - UserStats에 뉴스 통계 필드 추가 - 6개 뉴스 배지 Strategy 클래스 생성 - 뉴스 읽기/퀴즈/단어수집 시 통계 업데이트 및 배지 체크 연동 * fix: add PATCH method to CORS AllowMethods * test: BadgeType 개수 테스트 수정 (15 -> 29) * fix: CORS PATCH 메서드 추가 * docs: 뉴스 기능 프론트엔드 연동 가이드 작성 * fix: NewsCollectionFunction에 Bedrock, Comprehend 권한 추가 * fix: add null check for collectWord request body - Add INVALID_REQUEST error code to NewsErrorCode - Check body and word field before accessing in collectWord() - Prevents NullPointerException when request body is malformed * feat: enhance stats API and bookmark response for frontend - Add DAILY stats update for news read/quiz/word collection - Add /stats/dashboard endpoint with frontend-requested format - today: wordsLearned, newsRead, quizzesTaken, wordsTotal - overall: totalWordsLearned, totalNewsRead, averageAccuracy, streaks - weeklyProgress: last 7 days with date/wordsLearned/newsRead - Add news-related fields to all stats API responses - Fix bookmark API to include full article details (title, summary, etc.) * feat: add category classification to news AI analysis - Add category field to AnalysisResult record - Update Bedrock prompt to classify articles into categories (WORLD, POLITICS, BUSINESS, TECH, SCIENCE, HEALTH, SPORTS, ENTERTAINMENT, LIFESTYLE) - Parse and set category from AI response - Set GSI2 (CATEGORY#) index when category is available * fix: add /stats/dashboard endpoint to template.yaml * fix: filter by ARTICLE# prefix in findById to avoid returning UserNewsRecord * docs: add News API troubleshooting guide * feat: add Cognito authorizer to News API and enhance keyword extraction - Set CognitoAuthorizer for all News API endpoints in template.yaml - Update Bedrock AI to extract keywords with meanings and examples - Add fallback to Comprehend for keyword extraction when Bedrock fails - Modify KeywordInfo model to include example field - Adjust AI prompt to include keyword extraction with examples - Update AnalysisResult to store keywords and parse them from AI response * Revert "Merge branch 'test' into prod" This reverts commit c1a958eac6799d5838a76e4078ae304bcdb62278, reversing changes made to a6662e0cfc46c6e12f20a89f0df285ff282160bc. * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * refactor : session_id가 null 체크 추가 * fix : sessionId NullPointerException 에러 수정 (#496) * feat : WebSocket Connect, Disconnect 핸들러 & SpeakingConnecion 모델 구현 * feat : WebSocket 메시지 처리 handler, service 구현 * feat : WebSocket 연결 정보 Repository 구현 * fix : 오타 수정 * refactor : websocket -> rest api 전환 * feat : speaking handler REST로 교체 * feat : speaking 관련 dto 생성 * refacotor : 기존 service 코드 로직 재사용 및 repository 리펙토링 * feat : speaking rest API 람다 함수 추가 * refactor : speaking service 재사용 * refactor : AI 영어 회화 연습 코드 리팩토링 * feat : handleChat 메서드 JsonNull 체크 푸가 * refactor : session_id가 null 체크 추가 * feat : template 환경변수 리펙토링 --------- * feat: enhance bookmark and reading status tracking for News API - Add bookmark and reading status to individual article responses - Include bookmark status in paginated news responses - Modify `KeywordInfo` to support Korean translations (`meaningKo`) - Update AI prompts to extract Korean meanings for keywords * feat: add category filtering to UserWord API with enhanced query logic - Introduce `category` filtering to `getUserWords` API - Update WordCategory enums to include "news" category - Apply category filter after enrichment with word info - Adjust query limits for bookmarked and incorrect words queries * feat: add default value for ExistingCognitoClientId in template.yaml - Set default to an empty string to ensure compatibility with templates using this parameter without explicitly specifying a value. * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: SpeakingHandler getStringOrNull 컴파일 에러 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * fix: Bedrock 키워드(meaningKo 포함)를 article에 저장하도록 수정 * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * feat: add dashboard stats API and enhance news stats tracking - Introduce `/stats/dashboard` API for retrieving integrated user stats (today, overall, weekly progress, and level distribution) - Update `UserStats` model to include additional fields for news stats (newsRead, newsQuizCompleted, newsQuizPerfect, newsWordsCollected, etc.) - Add atomic updates to track news reading, quiz, and word stats in `UserStatsRepository` - Modify CloudFormation `template.yaml` to register the new `/stats/dashboard` endpoint * refactor: format code with consistent indentation and spacing - Apply consistent formatting to improve code readability across multiple files - Adjust indentation, spacing, and alignment in model classes, DTOs, and repository methods * fix: filter by ARTICLE# prefix in findById to avoid returning bookmark records * feature : Speaking Table & Function template.yaml 파일에 추가 (#513) * fix: BadgeRepository 클라이언트 초기화 패턴 통일 - 개별 DynamoDbEnhancedClient 생성 대신 AwsClients.dynamoDbEnhanced() 싱글톤 사용 - 다른 Repository들과 동일한 패턴 적용 - 불필요한 import 제거 Closes #396 * feat: EnvConfig 유틸리티 추가 및 환경 변수 검증 적용 환경 변수 미설정 시 명확한 에러 메시지를 제공하는 EnvConfig 유틸리티를 추가하고, 기존 System.getenv 호출을 EnvConfig.getRequired/getOrDefault로 대체함. - EnvConfig: getRequired, getOrDefault, getIntOrDefault, getLongOrDefault 메서드 제공 - Lambda Cold Start 시점에 환경 변수 누락을 조기 감지 - 기존 Config 클래스(WebSocketConfig, RoomTokenConfig) EnvConfig 사용으로 통일 Closes #403 * refactor: TestService submitTest 메서드 책임 분리 submitTest 메서드를 단일 책임 원칙에 맞게 리팩토링: - gradeAnswers(): 답안 채점 및 결과 집계 - isAnswerCorrect(): 단일 답안 정답 여부 판단 - buildResultItem(): 결과 항목 생성 - saveTestResult(): 테스트 결과 저장 - GradingResult record: 채점 결과 캡슐화 TestService와 TestCommandService 모두 동일하게 적용 Closes #404 * refactor: 하드코딩된 설정값 환경 변수로 외부화 각 도메인별 Config 클래스를 생성하여 하드코딩된 값들을 환경 변수로 설정 가능하게 변경. 기본값이 있어 환경 변수 미설정 시에도 기존 동작 유지. ## 새로 추가된 Config 클래스 - GrammarConfig: SESSION_TTL_DAYS, MAX_HISTORY_MESSAGES, MAX_TOKENS 등 - GameConfig: TOTAL_ROUNDS, ROUND_TIME_LIMIT, QUICK_GUESS_THRESHOLD_MS - VocabularyConfig: NEW_WORDS_COUNT, REVIEW_WORDS_COUNT, 상태 전이 임계값 등 ## 지원하는 환경 변수 - GRAMMAR_SESSION_TTL_DAYS, GRAMMAR_MAX_HISTORY_MESSAGES, GRAMMAR_MAX_TOKENS - GAME_TOTAL_ROUNDS, GAME_ROUND_TIME_LIMIT, GAME_QUICK_GUESS_THRESHOLD_MS - VOCAB_NEW_WORDS_COUNT, VOCAB_REVIEW_WORDS_COUNT - VOCAB_TRANSITION_TO_REVIEWING, VOCAB_TRANSITION_TO_MASTERED Closes #406 * test: StudyLevel enum 단위 테스트 추가 * test: Difficulty enum 단위 테스트 추가 * test: StudyConfig 단위 테스트 추가 * test: EnvConfig 단위 테스트 추가 * test: PaginatedResult 단위 테스트 추가 * test: JsonUtil 단위 테스트 추가 * test: CursorUtil 단위 테스트 추가 * test: CommonErrorCode 단위 테스트 추가 * test: CommonException 단위 테스트 추가 * test: BadgeType enum 단위 테스트 추가 * test: BadgeKey 상수 단위 테스트 추가 * test: WordStatus enum 단위 테스트 추가 * test: TestType enum 단위 테스트 추가 * test: VocabularyConfig 단위 테스트 추가 * test: VocabKey 상수 단위 테스트 추가 * test: VocabularyErrorCode 단위 테스트 추가 * test: VocabularyException 단위 테스트 추가 * test: SpacedRepetitionContext 단위 테스트 추가 * test: GrammarLevel enum 단위 테스트 추가 * test: GrammarConfig 단위 테스트 추가 * test: GrammarErrorCode 단위 테스트 추가 * test: GrammarException 단위 테스트 추가 * test: GameStatus enum 단위 테스트 추가 * test: GameConfig 단위 테스트 추가 * test: ChattingErrorCode 단위 테스트 추가 * test: ChattingException 단위 테스트 추가 * fix: OPIc FeedbackResponse.java 문법 오류 수정 * style: AwsClients 코드 포맷팅 * style: EnvConfig 코드 포맷팅 * style: RoomTokenConfig 코드 포맷팅 * style: WebSocketConfig 코드 포맷팅 * style: JsonUtil 코드 포맷팅 * style: GameConfig 코드 포맷팅 * style: GrammarConfig 코드 포맷팅 * style: GrammarKey 코드 포맷팅 * style: GrammarErrorCode 코드 포맷팅 * style: GrammarException 코드 포맷팅 * style: BedrockGrammarCheckFactory 코드 포맷팅 * style: GrammarConversationService 코드 포맷팅 * style: FeedbackResponse 코드 포맷팅 * style: SessionReportResponse 코드 포맷팅 * style: SpeakingError 코드 포맷팅 * style: SpeakingErrorType 코드 포맷팅 * style: OPIcException 코드 포맷팅 * style: OPIcAnswer 코드 포맷팅 * style: OPIcQuestion 코드 포맷팅 * style: OPIcSession 코드 포맷팅 * style: OPIcRepository 코드 포맷팅 * style: FeedbackService 코드 포맷팅 * style: TranscribeProxyService 코드 포맷팅 * style: VocabularyConfig 코드 포맷팅 * style: DailyStudyCommandService 코드 포맷팅 * style: TestCommandService 코드 포맷팅 * style: TestService 코드 포맷팅 * style: CommonErrorCodeSpec 코드 포맷팅 * style: JsonUtilSpec 코드 포맷팅 * style: BadgeTypeSpec 코드 포맷팅 * style: ChattingErrorCodeSpec 코드 포맷팅 * style: GrammarLevelSpec 코드 포맷팅 * style: GrammarErrorCodeSpec 코드 포맷팅 * style: VocabularyErrorCodeSpec 코드 포맷팅 * feature : OPIc 세션관리 + 답변 처리 파이프라인 구현 (#413) * feat : OPIc 질문 & 세션 관련 dto 생성 * refactor : @DynamoDbBean 주석 추가 * feat : 주제 + 소주제 + 레벨로 오픽 질문 조회 추가 * feat : OPIc 세션, 질문, 답변 종합 handler 구현 * feat : 오픽 주제, 소주제별 seed 데이터 추가 * refactor : Transcribe API KEY 환경변수명 수정 * refactor : Bedrock에 사용하는 클로드 모델 변경 * refactor : S3 Key 대신 Proxy에서 요청하는 Base64 값으로 변환 * feat: GAME_START, ROUND_END 메시지에 serverTime 추가 - broadcastGameStart(): serverTime, roundDuration 필드 추가 - broadcastRoundEnd(): serverTime, roundStartTime, roundDuration 필드 추가 - GameService.endRound(): data에 roundStartTime, roundDuration 포함 - 타이머 동기화 버그 수정을 위한 서버 시간 제공 * feat: WebSocketMessageHelper 유틸리티 클래스 추가 - domain 필드 포함 메시지 생성 헬퍼 - DOMAIN_CHAT, DOMAIN_GAME 상수 정의 - buildChatMessage(), buildGameMessage() 메서드 * feat: 모든 WebSocket 메시지에 domain 필드 추가 - ScoreUpdateMessage에 domain 필드 추가 - broadcastGameStart에 domain:"game" 추가 - broadcastRoundEnd에 domain:"game" 추가 - broadcastCorrectAnswerMessage에 domain:"game" 추가 - handleCommandResult 시스템 메시지에 domain:"game" 추가 - handleRegularMessage 채팅 메시지에 domain:"chat" 추가 Closes #426, #427 * feat: GameSession 모델 클래스 생성 - DynamoDB Enhanced Client 어노테이션 적용 - GSI1: roomId로 활성 게임 세션 조회 가능 - 게임 상태 관리용 헬퍼 메서드 포함 Closes #428 * feat: GameSessionRepository 구현 - 기본 CRUD (save, findById, delete) - roomId로 활성/전체 게임 세션 조회 - 상태, 라운드, 점수 업데이트 메서드 - 정답자 추가, 힌트 사용, 게임 종료 처리 Closes #429 * refactor: ChatRoom에서 게임 필드 분리 - 게임 관련 필드 제거 (gameStatus, currentRound 등) - activeGameSessionId 필드 추가 - 게임 상태는 GameSession으로 분리됨 Note: 의존 코드 수정은 #431에서 진행 Closes #430 * refactor: GameSession 기반으로 전체 게임 로직 리팩토링 - GameService: ChatRoom 대신 GameSession 사용 - GameStatsService: GameSession 매개변수로 변경 - CommandService: GameSession 기반 점수 조회 - GameHandler: GameSession 기반 REST API - WebSocketMessageHandler: GameSession 기반 브로드캐스트 - GameStatusResponse, ScoreboardResponse: GameSession 매개변수 Closes #431 * feat: GameSessionHandler Lambda 및 게임 세션 API 구현 - POST /rooms/{roomId}/games - 게임 세션 생성 - GET /games/{gameSessionId} - 게임 상태 조회 (재접속용) - POST /games/{gameSessionId}/start - 게임 시작 - POST /games/{gameSessionId}/stop - 게임 종료 모든 응답에 serverTime 포함 (타이머 동기화) 출제자에게만 currentWord 포함 Closes #432, #433 * feat: 게임 시작 7분 후 자동 종료 기능 구현 - GameConfig에 gameTimeLimit() 메서드 추가 (기본값: 420초) - GameSchedulerClient 유틸리티 클래스 생성 (EventBridge Scheduler 연동) - GameService에 스케줄 생성/취소 로직 추가 - GameAutoCloseHandler Lambda 함수 생성 - template.yaml에 Lambda, IAM Role, Schedule Group 추가 Closes #417 * fix : 메모리 증가 및 Lambda 응답 제한 시간 Cognito 트리거 제한시간과 동일하게 수정 (#439) * feat: 캐치마인드 게임 방 분리 기능 구현 (#455) - Room 타입 분리 (CHAT/GAME) - RoomType, RoomStatus enum 추가 - ChatRoom 모델에 type, gameType, gameSettings, status, hostId 필드 추가 - GameSettings 모델 추가 - 방 생성/조회 API 수정 - CreateRoomRequest에 type, gameType, gameSettings 필드 추가 - 방 목록 조회 시 type, gameType, status 필터 지원 - 게임 시작 조건 검증 - GAME 타입 방에서만 게임 시작 가능 (GAME_007 에러) - 게임 재시작 API 구현 (#452) - POST /rooms/{roomId}/game/restart 엔드포인트 추가 - 방장만 재시작 가능 (GAME_009 에러) - 게임 진행 중 재시작 불가 (GAME_008 에러) - 방장 변경 로직 구현 (#453) - 방장 퇴장 시 다음 멤버에게 자동 이전 - 모든 멤버 퇴장 시 방 자동 삭제 - WebSocket 메시지 타입 추가 (#454) - ROOM_STATUS_CHANGE, HOST_CHANGE 메시지 타입 추가 - WebSocketMessageHelper에 빌더 메서드 추가 - 테스트 추가 - RoomType, RoomStatus enum 테스트 - GameSettings 모델 테스트 - ChattingErrorCode 테스트 업데이트 Related: #440, #441, #442, #443, #444, #445, #446 Closes: #447, #448, #449, #450, #451, #452, #453, #454 * feat: 참가자 닉네임 및 방장 변경 WebSocket 알림 구현 (#456) - RoomParticipant DTO 추가 (userId, nickname, isHost) - ChatRoomQueryService에 닉네임 조회 메서드 추가 - getParticipantsWithNicknames(): 참가자 목록 + 닉네임 - getHostNickname(): 방장 닉네임 조회 - ChatRoomHandler.getRoom() 응답에 participants, hostNickname 추가 - ChatRoomCommandService.leaveRoom()에서 방장 변경 시 WebSocket 브로드캐스트 Related: #440 * fix: ChatRoomFunction에 UserTable DynamoDB 권한 추가 - 참가자/방장 닉네임 조회를 위한 UserTable 읽기 권한 추가 - DynamoDBReadPolicy로 UserTable 접근 허용 Fixes #457 * fix: GameSettings에 @DynamoDbBean 어노테이션 추가 - DynamoDB Enhanced Client가 중첩 객체를 직렬화/역직렬화할 수 있도록 수정 - ChatRoom 저장/조회 시 GameSettings 변환 오류 해결 Fixes #457 * fix: ChatRoomFunction에 WEBSOCKET_ENDPOINT 환경변수 및 권한 추가 - leaveRoom에서 WebSocketBroadcaster 사용을 위한 환경변수 추가 - execute-api:ManageConnections 권한 추가 * fix: WebSocket Lambda 함수들에 WEBSOCKET_ENDPOINT 환… Co-authored-by: ddingjoo Co-authored-by: Claude Opus 4.5 --- ServerlessFunction/build.gradle | 1 + .../serverless/common/config/AwsClients.java | 8 + .../opic/handler/OPIcSessionHandler.java | 19 ++ .../domain/opic/service/EmailService.java | 294 ++++++++++++++++++ ServerlessFunction/template.yaml | 7 + 5 files changed, 329 insertions(+) create mode 100644 ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/service/EmailService.java diff --git a/ServerlessFunction/build.gradle b/ServerlessFunction/build.gradle index 34615a92..b8f86d93 100644 --- a/ServerlessFunction/build.gradle +++ b/ServerlessFunction/build.gradle @@ -36,6 +36,7 @@ dependencies { implementation 'software.amazon.awssdk:ssm' implementation 'software.amazon.awssdk:scheduler' implementation 'software.amazon.awssdk:sqs' + implementation 'software.amazon.awssdk:ses' // AWS X-Ray SDK (다운스트림 서비스 추적용) implementation 'com.amazonaws:aws-xray-recorder-sdk-core:2.15.0' diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/common/config/AwsClients.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/common/config/AwsClients.java index a1d2286b..0b7416ba 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/common/config/AwsClients.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/common/config/AwsClients.java @@ -10,6 +10,7 @@ import software.amazon.awssdk.services.polly.PollyClient; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.services.ses.SesClient; import software.amazon.awssdk.services.sns.SnsClient; import software.amazon.awssdk.services.sqs.SqsClient; import software.amazon.awssdk.services.ssm.SsmClient; @@ -67,6 +68,11 @@ public final class AwsClients { .overrideConfiguration(XRAY_CONFIG) .build(); + // SES + private static final SesClient SES_CLIENT = SesClient.builder() + .overrideConfiguration(XRAY_CONFIG) + .build(); + private AwsClients() { // 인스턴스화 방지 } @@ -114,4 +120,6 @@ public static SsmClient ssm() { public static SqsClient sqs() { return SQS_CLIENT; } + + public static SesClient ses() { return SES_CLIENT; } } diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java index cd7335d4..659ba648 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java @@ -7,6 +7,7 @@ import com.google.gson.*; import com.mzc.secondproject.serverless.common.config.AwsClients; import com.mzc.secondproject.serverless.common.service.PollyService; +import com.mzc.secondproject.serverless.common.util.CognitoUtil; import com.mzc.secondproject.serverless.common.util.JwtUtil; import com.mzc.secondproject.serverless.common.util.ResponseGenerator; import com.mzc.secondproject.serverless.domain.opic.dto.request.CreateSessionRequest; @@ -18,6 +19,7 @@ import com.mzc.secondproject.serverless.domain.opic.model.OPIcQuestion; import com.mzc.secondproject.serverless.domain.opic.model.OPIcSession; import com.mzc.secondproject.serverless.domain.opic.repository.OPIcRepository; +import com.mzc.secondproject.serverless.domain.opic.service.EmailService; import com.mzc.secondproject.serverless.domain.opic.service.FeedbackService; import com.mzc.secondproject.serverless.domain.opic.service.TranscribeProxyService; import org.slf4j.Logger; @@ -52,12 +54,15 @@ public class OPIcSessionHandler implements RequestHandler"); + html.append(""); + html.append(""); + + // Header + html.append("

"); + html.append("

🎯 OPIc 스피킹 테스트 결과

"); + html.append("
"); + + // Main Content + html.append("
"); + + // Greeting + html.append("

안녕하세요, ") + .append(userName != null ? userName : "학습자") + .append("님!

"); + html.append("

OPIc 스피킹 테스트 결과를 알려드립니다.

"); + + // Score Cards + html.append("
"); + + // Estimated Level + html.append("
"); + html.append("

예상 등급

"); + html.append("

") + .append(report.estimatedLevel()).append("

"); + html.append("
"); + + // Overall Score + html.append("
"); + html.append("

종합 점수

"); + html.append("

") + .append(report.overallScore()).append("

"); + html.append("
"); + + html.append("
"); + + // Feedback + html.append("
"); + html.append("

📝 종합 피드백

"); + html.append("

") + .append(report.feedback()).append("

"); + html.append("
"); + + // Strengths + html.append("
"); + html.append("

💪 잘한 점

"); + html.append("
    "); + for (String strength : report.strengths()) { + html.append("
  • ").append(strength).append("
  • "); + } + html.append("
"); + html.append("
"); + + // Weaknesses + html.append("
"); + html.append("

📈 개선할 점

"); + html.append("
    "); + for (String weakness : report.weaknesses()) { + html.append("
  • ").append(weakness).append("
  • "); + } + html.append("
"); + html.append("
"); + + // Recommendations + html.append("
"); + html.append("

💡 학습 추천

"); + html.append("
    "); + for (String rec : report.recommendations()) { + html.append("
  1. ").append(rec).append("
  2. "); + } + html.append("
"); + html.append("
"); + + // CTA Button + html.append("
"); + html.append(""); + html.append("전체 리포트 보기"); + html.append("
"); + + // Footer + html.append("
"); + html.append("

"); + html.append("본 이메일은 English Study 서비스에서 자동으로 발송되었습니다.
"); + html.append("© 2025 English Study. All rights reserved.

"); + + html.append("
"); + html.append(""); + + return html.toString(); + } + + /** + * OPIc 리포트 텍스트 버전 (HTML 미지원 이메일 클라이언트용) + */ + private String buildOPIcReportText(String userName, SessionReportResponse report) { + StringBuilder text = new StringBuilder(); + + text.append("OPIc 스피킹 테스트 결과\n"); + text.append("================================\n\n"); + + text.append("안녕하세요, ").append(userName != null ? userName : "학습자").append("님!\n"); + text.append("OPIc 스피킹 테스트 결과를 알려드립니다.\n\n"); + + text.append("결과 요약\n"); + text.append("------------\n"); + text.append("예상 등급: ").append(report.estimatedLevel()).append("\n"); + text.append("종합 점수: ").append(report.overallScore()).append("점\n\n"); + + text.append("종합 피드백\n"); + text.append("------------\n"); + text.append(report.feedback()).append("\n\n"); + + text.append("잘한 점\n"); + text.append("------------\n"); + for (String strength : report.strengths()) { + text.append("• ").append(strength).append("\n"); + } + text.append("\n"); + + text.append("개선할 점\n"); + text.append("------------\n"); + for (String weakness : report.weaknesses()) { + text.append("• ").append(weakness).append("\n"); + } + text.append("\n"); + + text.append("학습 추천\n"); + text.append("------------\n"); + int i = 1; + for (String rec : report.recommendations()) { + text.append(i++).append(". ").append(rec).append("\n"); + } + text.append("\n"); + + text.append("================================\n"); + text.append("© 2025 English Study\n"); + + return text.toString(); + } + + /** + * 레벨별 색상 반환 + */ + private String getLevelColor(String level) { + return switch (level) { + case "NL", "NM", "NH" -> "#6b7280"; + case "IL" -> "#22c55e"; + case "IM1" -> "#10b981"; + case "IM2" -> "#3b82f6"; + case "IM3" -> "#8b5cf6"; + case "IH" -> "#f97316"; + case "AL" -> "#ef4444"; + default -> "#3b82f6"; + }; + } + + /** + * 점수별 색상 반환 + */ + private String getScoreColor(int score) { + if (score >= 90) return "#059669"; + if (score >= 70) return "#3b82f6"; + if (score >= 50) return "#f97316"; + return "#ef4444"; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index 385d914e..5c12616b 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -49,6 +49,7 @@ Globals: AWS_REGION_NAME: !Ref AWS::Region ROOM_TOKEN_TTL_SECONDS: "300" TRANSCRIBE_PROXY_URL: "https://tfo1zm7vec.execute-api.ap-northeast-2.amazonaws.com/prod/transcribe" + SES_SENDER_EMAIL: "hye.ina0130@gmail.com" Api: TracingEnabled: true @@ -1554,6 +1555,12 @@ Resources: Action: - ssm:GetParameter Resource: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/opic/*" + - Statement: + - Effect: Allow + Action: + - ses:SendEmail + - ses:SendRawEmail + Resource: "*" - SNSPublishMessagePolicy: TopicName: !GetAtt NotificationTopic.TopicName Events: