From cfc417500f0e90f3f6eb1eaa0780aedc873920aa Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Sun, 25 Jan 2026 18:48:54 +0900 Subject: [PATCH 1/3] fix: force Authorizer recreation to update Cognito User Pool --- ServerlessFunction/template.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index 225dc20..ab3187e 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -147,6 +147,7 @@ Resources: Authorizers: CognitoAuthorizer: UserPoolArn: !Sub "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${ExistingCognitoUserPoolId}" + AuthorizationScopes: [] Identity: Header: Authorization From 81ab3cbb4cb1325d31bd2532266ab1ca15e4d5a8 Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Sun, 25 Jan 2026 19:13:00 +0900 Subject: [PATCH 2/3] fix: rename Authorizer to CognitoAuthV2 to force recreation with correct User Pool --- ServerlessFunction/template.yaml | 133 +++++++++++++++---------------- 1 file changed, 66 insertions(+), 67 deletions(-) diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index ab3187e..ed741ae 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -142,12 +142,11 @@ Resources: ResponseTemplates: application/json: '{"message": "Token expired", "statusCode": 401}' Auth: - DefaultAuthorizer: CognitoAuthorizer + DefaultAuthorizer: CognitoAuthV2 AddDefaultAuthorizerToCorsPreflight: false Authorizers: - CognitoAuthorizer: + CognitoAuthV2: UserPoolArn: !Sub "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${ExistingCognitoUserPoolId}" - AuthorizationScopes: [] Identity: Header: Authorization @@ -403,7 +402,7 @@ Resources: Path: /chat/rooms Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetRooms: Type: Api Properties: @@ -411,7 +410,7 @@ Resources: Path: /chat/rooms Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetRoom: Type: Api Properties: @@ -419,7 +418,7 @@ Resources: Path: /chat/rooms/{roomId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DeleteRoom: Type: Api Properties: @@ -427,7 +426,7 @@ Resources: Path: /chat/rooms/{roomId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 JoinRoom: Type: Api Properties: @@ -435,7 +434,7 @@ Resources: Path: /chat/rooms/{roomId}/join Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 LeaveRoom: Type: Api Properties: @@ -443,7 +442,7 @@ Resources: Path: /chat/rooms/{roomId}/leave Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GameFunction: Type: AWS::Serverless::Function @@ -490,7 +489,7 @@ Resources: Path: /chat/rooms/{roomId}/game/start Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 StopGame: Type: Api Properties: @@ -498,7 +497,7 @@ Resources: Path: /chat/rooms/{roomId}/game/stop Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetGameStatus: Type: Api Properties: @@ -506,7 +505,7 @@ Resources: Path: /chat/rooms/{roomId}/game/status Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetScores: Type: Api Properties: @@ -514,7 +513,7 @@ Resources: Path: /chat/rooms/{roomId}/game/scores Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 끝말잇기(Word Chain) 게임 핸들러 WordChainFunction: @@ -547,7 +546,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/start Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 SubmitWord: Type: Api Properties: @@ -555,7 +554,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/submit Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 HandleTimeout: Type: Api Properties: @@ -563,7 +562,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/timeout Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 StopWordChain: Type: Api Properties: @@ -571,7 +570,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/stop Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetWordChainStatus: Type: Api Properties: @@ -579,7 +578,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/status Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 게임 자동 종료 Lambda (EventBridge Scheduler에 의해 호출) GameAutoCloseFunction: @@ -667,7 +666,7 @@ Resources: Path: /chat/rooms/{roomId}/messages Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetMessages: Type: Api Properties: @@ -675,7 +674,7 @@ Resources: Path: /chat/rooms/{roomId}/messages Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetMessage: Type: Api Properties: @@ -683,7 +682,7 @@ Resources: Path: /chat/rooms/{roomId}/messages/{messageId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ChatVoiceFunction: Type: AWS::Serverless::Function @@ -713,7 +712,7 @@ Resources: Path: /chat/voice/synthesize Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Vocabulary Lambda Functions @@ -817,7 +816,7 @@ Resources: Path: /vocab/wrong-answers Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetUserWords: Type: Api Properties: @@ -825,7 +824,7 @@ Resources: Path: /vocab/user-words Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetUserWord: Type: Api Properties: @@ -833,7 +832,7 @@ Resources: Path: /vocab/user-words/{wordId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateUserWord: Type: Api Properties: @@ -841,7 +840,7 @@ Resources: Path: /vocab/user-words/{wordId} Method: PUT Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateUserWordTag: Type: Api Properties: @@ -849,7 +848,7 @@ Resources: Path: /vocab/user-words/{wordId}/tag Method: PATCH Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateUserWordStatus: Type: Api Properties: @@ -857,7 +856,7 @@ Resources: Path: /vocab/user-words/{wordId}/status Method: PATCH Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 WordGroupFunction: Type: AWS::Serverless::Function @@ -879,7 +878,7 @@ Resources: Path: /vocab/groups Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetGroups: Type: Api Properties: @@ -887,7 +886,7 @@ Resources: Path: /vocab/groups Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetGroupDetail: Type: Api Properties: @@ -895,7 +894,7 @@ Resources: Path: /vocab/groups/{groupId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateGroup: Type: Api Properties: @@ -903,7 +902,7 @@ Resources: Path: /vocab/groups/{groupId} Method: PUT Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DeleteGroup: Type: Api Properties: @@ -911,7 +910,7 @@ Resources: Path: /vocab/groups/{groupId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 AddWordToGroup: Type: Api Properties: @@ -919,7 +918,7 @@ Resources: Path: /vocab/groups/{groupId}/words/{wordId} Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 RemoveWordFromGroup: Type: Api Properties: @@ -927,7 +926,7 @@ Resources: Path: /vocab/groups/{groupId}/words/{wordId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DailyStudyFunction: Type: AWS::Serverless::Function @@ -954,7 +953,7 @@ Resources: Path: /vocab/daily Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 MarkWordLearned: Type: Api Properties: @@ -962,7 +961,7 @@ Resources: Path: /vocab/daily/words/{wordId}/learned Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 TestFunction: Type: AWS::Serverless::Function @@ -992,7 +991,7 @@ Resources: Path: /vocab/test/start Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 SubmitAnswer: Type: Api Properties: @@ -1000,7 +999,7 @@ Resources: Path: /vocab/test/submit Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTestResults: Type: Api Properties: @@ -1008,7 +1007,7 @@ Resources: Path: /vocab/test/results Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTestResultDetail: Type: Api Properties: @@ -1016,7 +1015,7 @@ Resources: Path: /vocab/test/results/{testId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTestedWords: Type: Api Properties: @@ -1024,7 +1023,7 @@ Resources: Path: /vocab/test/tested-words Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 StatsFunction: Type: AWS::Serverless::Function @@ -1046,7 +1045,7 @@ Resources: Path: /vocab/stats Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetDailyStats: Type: Api Properties: @@ -1054,7 +1053,7 @@ Resources: Path: /vocab/stats/daily Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetWeaknessAnalysis: Type: Api Properties: @@ -1062,7 +1061,7 @@ Resources: Path: /vocab/stats/weakness Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 VocabVoiceFunction: Type: AWS::Serverless::Function @@ -1144,7 +1143,7 @@ Resources: Path: /stats/daily Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetWeeklyStats: Type: Api Properties: @@ -1152,7 +1151,7 @@ Resources: Path: /stats/weekly Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetMonthlyStats: Type: Api Properties: @@ -1160,7 +1159,7 @@ Resources: Path: /stats/monthly Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTotalStats: Type: Api Properties: @@ -1168,7 +1167,7 @@ Resources: Path: /stats/total Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetDashboard: Type: Api Properties: @@ -1176,7 +1175,7 @@ Resources: Path: /stats/dashboard Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetStatsHistory: Type: Api Properties: @@ -1184,7 +1183,7 @@ Resources: Path: /stats/history Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # Badge Lambda Function BadgeFunction: @@ -1214,7 +1213,7 @@ Resources: Path: /badges Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetEarnedBadges: Type: Api Properties: @@ -1222,7 +1221,7 @@ Resources: Path: /badges/earned Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Grammar Lambda Functions @@ -1269,7 +1268,7 @@ Resources: Path: /grammar/check Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GrammarConversation: Type: Api Properties: @@ -1277,7 +1276,7 @@ Resources: Path: /grammar/conversation Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetSessions: Type: Api Properties: @@ -1285,7 +1284,7 @@ Resources: Path: /grammar/sessions Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetSessionDetail: Type: Api Properties: @@ -1293,7 +1292,7 @@ Resources: Path: /grammar/sessions/{sessionId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DeleteSession: Type: Api Properties: @@ -1301,7 +1300,7 @@ Resources: Path: /grammar/sessions/{sessionId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Grammar WebSocket API (Streaming) @@ -1565,7 +1564,7 @@ Resources: Path: /opic/sessions Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 세션 조회 GetSession: Type: Api @@ -1574,7 +1573,7 @@ Resources: Path: /opic/sessions/{sessionId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 세션 목록 조회 GetSessions: Type: Api @@ -1583,7 +1582,7 @@ Resources: Path: /opic/sessions Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 다음 질문 조회 GetNextQuestion: Type: Api @@ -1592,7 +1591,7 @@ Resources: Path: /opic/sessions/{sessionId}/questions/next Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 답변 제출 SubmitAnswer: Type: Api @@ -1601,7 +1600,7 @@ Resources: Path: /opic/sessions/{sessionId}/answers Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 세션 완료 CompleteSession: Type: Api @@ -1610,7 +1609,7 @@ Resources: Path: /opic/sessions/{sessionId}/complete Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 음성 업로드 Presigned URL GetUploadUrl: Type: Api @@ -1619,7 +1618,7 @@ Resources: Path: /opic/sessions/{sessionId}/upload-url Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Speaking Lambda Functions @@ -1661,7 +1660,7 @@ Resources: Path: /speaking/chat Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 SpeakingReset: Type: Api Properties: @@ -1669,7 +1668,7 @@ Resources: Path: /speaking/reset Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # DynamoDB Tables From c03c3c9bedbb93102c6c4c89fa52dd99d06375a3 Mon Sep 17 00:00:00 2001 From: ddingjoo Date: Sun, 25 Jan 2026 19:23:39 +0900 Subject: [PATCH 3/3] fix: use GSI1 index for WordChainSession roomId queries findActiveByRoomId was querying main table with ROOM#roomId partition key, but the main table PK is WORDCHAIN#sessionId. Fixed to use GSI1 index which has ROOM#roomId as partition key. --- .../repository/WordChainSessionRepository.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java index ca39ddf..b871987 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java @@ -5,6 +5,7 @@ import com.mzc.secondproject.serverless.domain.chatting.model.WordChainSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbIndex; import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; import software.amazon.awssdk.enhanced.dynamodb.Key; import software.amazon.awssdk.enhanced.dynamodb.TableSchema; @@ -19,16 +20,20 @@ public class WordChainSessionRepository { private static final Logger logger = LoggerFactory.getLogger(WordChainSessionRepository.class); private static final String TABLE_NAME = EnvConfig.getRequired("CHAT_TABLE_NAME"); + private static final String GSI1_INDEX_NAME = "GSI1"; private final DynamoDbTable table; + private final DynamoDbIndex gsi1Index; public WordChainSessionRepository() { this.table = AwsClients.dynamoDbEnhanced() .table(TABLE_NAME, TableSchema.fromBean(WordChainSession.class)); + this.gsi1Index = table.index(GSI1_INDEX_NAME); } public WordChainSessionRepository(DynamoDbTable table) { this.table = table; + this.gsi1Index = table.index(GSI1_INDEX_NAME); } /** @@ -52,16 +57,16 @@ public Optional findById(String sessionId) { } /** - * 방의 활성 세션 조회 + * 방의 활성 세션 조회 (GSI1 인덱스 사용) */ public Optional findActiveByRoomId(String roomId) { - return table.query(QueryConditional.sortBeginsWith( + return gsi1Index.query(QueryConditional.sortBeginsWith( Key.builder() .partitionValue("ROOM#" + roomId) .sortValue("WORDCHAIN#") .build())) - .items() .stream() + .flatMap(page -> page.items().stream()) .filter(WordChainSession::isActive) .findFirst(); }