From f1d5aa6852f4448ff627a8856e1ec65a4017b4ce Mon Sep 17 00:00:00 2001 From: hyein Heo <128613248+hye-inA@users.noreply.github.com> Date: Sun, 25 Jan 2026 02:45:32 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feature=20=20:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EC=9A=A9=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#534)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 에러) - 방장 변경 로직 구현 (#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 * feature : 말하기 연습 기능 polly 서비스 권한 추가 (#514) * 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 * feature : transcribe API KEY 추가 (#516) * 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 * feat : 채팅 도메인 닉네임 조회용 메서드 추가 --------- Co-authored-by: ddingjoo --- .../websocket/WebSocketMessageHandler.java | 20 ++ .../domain/chatting/model/ChatMessage.java | 1 + .../handler/SpeakingConnectHandler.java | 0 .../handler/SpeakingDisconnectHandler.java | 0 .../speaking/handler/SpeakingHandler.java | 271 +++++++++--------- .../handler/SpeakingMessageHandler.java | 0 .../SpeakingConnectionRepository.java | 0 .../repository/SpeakingSessionRepository.java | 116 ++++---- .../domain/user/service/UserService.java | 10 + ServerlessFunction/template.yaml | 88 ++++++ 10 files changed, 314 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/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/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/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 84ae5559..225dc206 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -37,12 +37,14 @@ 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}" 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" @@ -1618,6 +1620,56 @@ 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 + 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: + - bedrock:InvokeModel + - polly:SynthesizeSpeech + 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 ############################################# @@ -2007,6 +2059,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 ############################################# @@ -2236,6 +2320,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 4341f8e95150db1a384d4a736bb680a69d5476ee Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Sun, 25 Jan 2026 17:30:01 +0900 Subject: [PATCH 2/2] fix: add Cognito parameters to CI/CD buildspec and wordchain turnTimeLimit support - Add ExistingCognitoUserPoolId and ExistingCognitoClientId to buildspec-dev.yml and buildspec-prod.yml - Add turnTimeLimit field to GameSettings for wordchain game - Add baseTurnTimeLimit to WordChainSession for dynamic time calculation - Update WordChainService to read turnTimeLimit from room's gameSettings --- ServerlessFunction/buildspec-dev.yml | 5 +++- ServerlessFunction/buildspec-prod.yml | 5 +++- .../domain/chatting/model/GameSettings.java | 7 ++++-- .../chatting/model/WordChainSession.java | 22 ++++++++++++++-- .../chatting/service/WordChainService.java | 25 ++++++++++++++++--- 5 files changed, 55 insertions(+), 9 deletions(-) diff --git a/ServerlessFunction/buildspec-dev.yml b/ServerlessFunction/buildspec-dev.yml index 78a12758..10571c8d 100644 --- a/ServerlessFunction/buildspec-dev.yml +++ b/ServerlessFunction/buildspec-dev.yml @@ -43,7 +43,10 @@ phases: --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \ --no-confirm-changeset \ --no-fail-on-empty-changeset \ - --parameter-overrides Environment=$ENVIRONMENT + --parameter-overrides \ + Environment=$ENVIRONMENT \ + ExistingCognitoUserPoolId=ap-northeast-2_ezDwzFCzR \ + ExistingCognitoClientId=4ns077jcr1pkue2vvisr6qdpu5 - echo "Deployment completed on $(date)" cache: diff --git a/ServerlessFunction/buildspec-prod.yml b/ServerlessFunction/buildspec-prod.yml index 0aa83787..6b8ed3e3 100644 --- a/ServerlessFunction/buildspec-prod.yml +++ b/ServerlessFunction/buildspec-prod.yml @@ -43,7 +43,10 @@ phases: --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \ --no-confirm-changeset \ --no-fail-on-empty-changeset \ - --parameter-overrides Environment=$ENVIRONMENT + --parameter-overrides \ + Environment=$ENVIRONMENT \ + ExistingCognitoUserPoolId=ap-northeast-2_ezDwzFCzR \ + ExistingCognitoClientId=4ns077jcr1pkue2vvisr6qdpu5 - echo "Deployment completed on $(date)" cache: diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/GameSettings.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/GameSettings.java index d97f2fcc..7c2ae723 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/GameSettings.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/GameSettings.java @@ -14,10 +14,13 @@ public class GameSettings { @Builder.Default private Integer maxRounds = 5; - + @Builder.Default private Integer roundTimeLimit = 60; - + + @Builder.Default + private Integer turnTimeLimit = 15; // 끝말잇기용 턴 시간 제한 + @Builder.Default private Boolean autoDeleteOnEnd = false; } diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/WordChainSession.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/WordChainSession.java index aa4e2c8d..62f49483 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/WordChainSession.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/WordChainSession.java @@ -40,6 +40,7 @@ public class WordChainSession { private Character nextLetter; // 다음 사람이 시작해야 할 글자 private Long turnStartTime; private Integer timeLimit; // 현재 라운드 시간 제한 (초) + private Integer baseTurnTimeLimit; // 사용자 설정 기본 턴 시간 (초) // 플레이어 관리 private List players; // 전체 플레이어 (순서대로) @@ -182,10 +183,27 @@ public String getWinner() { /** * 시간 제한 계산 (라운드에 따라 점점 빨라짐) - * Round 1-2: 15초, Round 3-4: 13초, Round 5-6: 11초, Round 7-8: 9초, Round 9+: 8초 + * 기본값 15초에서 시작하여 2라운드마다 2초씩 감소, 최소 8초 */ public static int calculateTimeLimit(int round) { - return Math.max(8, 15 - ((round - 1) / 2) * 2); + return calculateTimeLimit(round, 15); + } + + /** + * 시간 제한 계산 (기본 시간 제한 기준) + * 설정된 기본 시간에서 시작하여 2라운드마다 1초씩 감소, 최소 (baseTimeLimit / 2)초 + */ + public static int calculateTimeLimit(int round, int baseTimeLimit) { + int minTimeLimit = Math.max(5, baseTimeLimit / 2); + return Math.max(minTimeLimit, baseTimeLimit - ((round - 1) / 2)); + } + + /** + * 세션의 기본 시간 제한을 기준으로 다음 라운드 시간 계산 + */ + public int getNextRoundTimeLimit(int nextRound) { + int base = baseTurnTimeLimit != null ? baseTurnTimeLimit : 15; + return calculateTimeLimit(nextRound, base); } /** diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/service/WordChainService.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/service/WordChainService.java index c8298468..eb41e151 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/service/WordChainService.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/service/WordChainService.java @@ -1,7 +1,10 @@ package com.mzc.secondproject.serverless.domain.chatting.service; +import com.mzc.secondproject.serverless.domain.chatting.model.ChatRoom; import com.mzc.secondproject.serverless.domain.chatting.model.Connection; +import com.mzc.secondproject.serverless.domain.chatting.model.GameSettings; import com.mzc.secondproject.serverless.domain.chatting.model.WordChainSession; +import com.mzc.secondproject.serverless.domain.chatting.repository.ChatRoomRepository; import com.mzc.secondproject.serverless.domain.chatting.repository.ConnectionRepository; import com.mzc.secondproject.serverless.domain.chatting.repository.WordChainSessionRepository; import com.mzc.secondproject.serverless.domain.user.model.User; @@ -29,6 +32,7 @@ public class WordChainService { private final WordChainSessionRepository sessionRepository; private final ConnectionRepository connectionRepository; + private final ChatRoomRepository chatRoomRepository; private final UserRepository userRepository; private final DictionaryService dictionaryService; private final Random random; @@ -36,16 +40,19 @@ public class WordChainService { public WordChainService() { this(new WordChainSessionRepository(), new ConnectionRepository(), + new ChatRoomRepository(), new UserRepository(), new DictionaryService()); } public WordChainService(WordChainSessionRepository sessionRepository, ConnectionRepository connectionRepository, + ChatRoomRepository chatRoomRepository, UserRepository userRepository, DictionaryService dictionaryService) { this.sessionRepository = sessionRepository; this.connectionRepository = connectionRepository; + this.chatRoomRepository = chatRoomRepository; this.userRepository = userRepository; this.dictionaryService = dictionaryService; this.random = new Random(); @@ -67,6 +74,17 @@ public GameStartResult startGame(String roomId, String userId) { return GameStartResult.error("최소 2명 이상 필요합니다."); } + // 방 정보에서 gameSettings 조회 + Optional optRoom = chatRoomRepository.findById(roomId); + int baseTurnTimeLimit = 15; // 기본값 + if (optRoom.isPresent()) { + ChatRoom room = optRoom.get(); + GameSettings settings = room.getGameSettings(); + if (settings != null && settings.getTurnTimeLimit() != null) { + baseTurnTimeLimit = settings.getTurnTimeLimit(); + } + } + // 플레이어 순서 랜덤 셔플 List players = connections.stream() .map(Connection::getUserId) @@ -81,7 +99,7 @@ public GameStartResult startGame(String roomId, String userId) { String sessionId = UUID.randomUUID().toString(); String now = Instant.now().toString(); long currentTime = System.currentTimeMillis(); - int timeLimit = WordChainSession.calculateTimeLimit(1); + int timeLimit = baseTurnTimeLimit; // 사용자 설정 턴 시간 사용 WordChainSession session = WordChainSession.builder() .pk("WORDCHAIN#" + sessionId) @@ -100,6 +118,7 @@ public GameStartResult startGame(String roomId, String userId) { .nextLetter(nextLetter) .turnStartTime(currentTime) .timeLimit(timeLimit) + .baseTurnTimeLimit(baseTurnTimeLimit) .players(players) .activePlayers(new ArrayList<>(players)) .eliminatedPlayers(new ArrayList<>()) @@ -171,7 +190,7 @@ public WordSubmitResult submitWord(String roomId, String userId, String word) { char nextLetter = normalizedWord.charAt(normalizedWord.length() - 1); String nextPlayerId = session.getNextPlayerId(); int nextRound = session.getCurrentRound() + 1; - int nextTimeLimit = WordChainSession.calculateTimeLimit(nextRound); + int nextTimeLimit = session.getNextRoundTimeLimit(nextRound); session.setCurrentRound(nextRound); session.setCurrentWord(normalizedWord); @@ -227,7 +246,7 @@ private WordSubmitResult handleTimeout(WordChainSession session, String userId) // 다음 턴 준비 String nextPlayerId = session.getNextPlayerId(); int nextRound = session.getCurrentRound() + 1; - int nextTimeLimit = WordChainSession.calculateTimeLimit(nextRound); + int nextTimeLimit = session.getNextRoundTimeLimit(nextRound); session.setCurrentRound(nextRound); session.setCurrentPlayerId(nextPlayerId);