diff --git a/ServerlessFunction/buildspec-prod.yml b/ServerlessFunction/buildspec-prod.yml index 6b8ed3e3..25d01611 100644 --- a/ServerlessFunction/buildspec-prod.yml +++ b/ServerlessFunction/buildspec-prod.yml @@ -5,7 +5,6 @@ env: SAM_CLI_TELEMETRY: 0 GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.parallel=true -Dorg.gradle.caching=true" ENVIRONMENT: prod - STACK_NAME: group2-englishstudy-prod phases: install: @@ -28,26 +27,17 @@ phases: - echo "Building SAM application for $ENVIRONMENT..." - cd $CODEBUILD_SRC_DIR/ServerlessFunction - sam build --parallel --cached - - echo "Build completed" + - echo "Packaging SAM application..." + - sam package --s3-bucket group2-englishstudy-pipeline-artifacts --s3-prefix sam-packages/$ENVIRONMENT --output-template-file packaged-template.yaml post_build: commands: - - echo "Deploying to $ENVIRONMENT environment..." - - cd $CODEBUILD_SRC_DIR/ServerlessFunction - - | - sam deploy \ - --stack-name $STACK_NAME \ - --s3-bucket group2-englishstudy-pipeline-artifacts \ - --s3-prefix sam-deploy/prod \ - --region ap-northeast-2 \ - --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \ - --no-confirm-changeset \ - --no-fail-on-empty-changeset \ - --parameter-overrides \ - Environment=$ENVIRONMENT \ - ExistingCognitoUserPoolId=ap-northeast-2_ezDwzFCzR \ - ExistingCognitoClientId=4ns077jcr1pkue2vvisr6qdpu5 - - echo "Deployment completed on $(date)" + - echo "Build completed on $(date)" + +artifacts: + files: + - packaged-template.yaml + base-directory: ServerlessFunction cache: paths: diff --git a/ServerlessFunction/buildspec-test.yml b/ServerlessFunction/buildspec-test.yml index e00db1d6..6d75e4f0 100644 --- a/ServerlessFunction/buildspec-test.yml +++ b/ServerlessFunction/buildspec-test.yml @@ -5,7 +5,6 @@ env: SAM_CLI_TELEMETRY: 0 GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.parallel=true -Dorg.gradle.caching=true" ENVIRONMENT: test - STACK_NAME: group2-englishstudy-test phases: install: @@ -28,23 +27,17 @@ phases: - echo "Building SAM application for $ENVIRONMENT..." - cd $CODEBUILD_SRC_DIR/ServerlessFunction - sam build --parallel --cached - - echo "Build completed" + - echo "Packaging SAM application..." + - sam package --s3-bucket group2-englishstudy-pipeline-artifacts --s3-prefix sam-packages/$ENVIRONMENT --output-template-file packaged-template.yaml post_build: commands: - - echo "Deploying to $ENVIRONMENT environment..." - - cd $CODEBUILD_SRC_DIR/ServerlessFunction - - | - sam deploy \ - --stack-name $STACK_NAME \ - --resolve-s3 \ - --s3-prefix $STACK_NAME \ - --region ap-northeast-2 \ - --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND \ - --no-confirm-changeset \ - --no-fail-on-empty-changeset \ - --parameter-overrides Environment=$ENVIRONMENT ExistingCognitoUserPoolId=ap-northeast-2_ezDwzFCzR ExistingCognitoClientId=4ns077jcr1pkue2vvisr6qdpu5 - - echo "Deployment completed on $(date)" + - echo "Build completed on $(date)" + +artifacts: + files: + - packaged-template.yaml + base-directory: ServerlessFunction cache: paths: diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/ChatMessageHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/ChatMessageHandler.java index b156feba..07ef6ac9 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/ChatMessageHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/ChatMessageHandler.java @@ -14,6 +14,8 @@ import com.mzc.secondproject.serverless.domain.chatting.model.ChatMessage; import com.mzc.secondproject.serverless.domain.chatting.repository.ChatRoomRepository; import com.mzc.secondproject.serverless.domain.chatting.service.ChatMessageService; +import com.mzc.secondproject.serverless.domain.user.model.User; +import com.mzc.secondproject.serverless.domain.user.service.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,21 +31,23 @@ public class ChatMessageHandler implements RequestHandler handleRequest(Map event, Context cont RoomToken token = optToken.get(); String userId = token.getUserId(); String roomId = token.getRoomId(); - + String nickname = "Unknown"; + + // Cognito Authorizer에서 닉네임 추출 + try { + if (event.containsKey("requestContext")) { + Map reqCtx = (Map) event.get("requestContext"); + + if (reqCtx.containsKey("authorizer")) { + Map auth = (Map) reqCtx.get("authorizer"); + + Map claims = auth; + if (auth.containsKey("claims")) { + claims = (Map) auth.get("claims"); + } else if (auth.containsKey("principalId")) { + claims = auth; + } + + if (claims != null) { + if (claims.get("nickname") != null) { + nickname = (String) claims.get("nickname"); + } else if (claims.get("custom:nickname") != null) { + nickname = (String) claims.get("custom:nickname"); + } else if (claims.get("name") != null) { + nickname = (String) claims.get("name"); + } + } + } + } + // 닉네임 못찾았으면 UserId 앞부분 표시 + if ("Unknown".equals(nickname) && userId != null && userId.length() > 5) { + nickname = "User-" + userId.substring(0, 5); + } + } catch (Exception ex) { + logger.warn("닉네임 표시 실패: {}", ex.getMessage()); + } + // 같은 방에서 기존 연결 삭제 (새로고침 시 중복 연결 방지) connectionRepository.deleteUserConnectionsInRoom(userId, roomId); @@ -72,6 +107,7 @@ public Map handleRequest(Map event, Context cont .gsi2sk("CONN#" + connectionId) .connectionId(connectionId) .userId(userId) + .nickname(nickname) .roomId(roomId) .connectedAt(now) .ttl(ttl) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java index 0435d5cb..c490e093 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/handler/websocket/WebSocketMessageHandler.java @@ -202,6 +202,7 @@ private Map handleRegularMessage(String connectionId, MessagePay broadcastMessage.put("messageId", savedMessage.getMessageId()); broadcastMessage.put("roomId", savedMessage.getRoomId()); broadcastMessage.put("userId", savedMessage.getUserId()); + broadcastMessage.put("nickname", savedMessage.getNickname()); broadcastMessage.put("content", savedMessage.getContent()); broadcastMessage.put("messageType", savedMessage.getMessageType()); broadcastMessage.put("createdAt", savedMessage.getCreatedAt()); diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/Connection.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/Connection.java index 7c130390..5aa02e20 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/Connection.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/model/Connection.java @@ -22,6 +22,7 @@ public class Connection { private String connectionId; private String userId; + private String nickname; private String roomId; private String connectedAt; private Long ttl; // 10분 후 자동 삭제 diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java index ca39ddf7..b871987f 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/chatting/repository/WordChainSessionRepository.java @@ -5,6 +5,7 @@ import com.mzc.secondproject.serverless.domain.chatting.model.WordChainSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbIndex; import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; import software.amazon.awssdk.enhanced.dynamodb.Key; import software.amazon.awssdk.enhanced.dynamodb.TableSchema; @@ -19,16 +20,20 @@ public class WordChainSessionRepository { private static final Logger logger = LoggerFactory.getLogger(WordChainSessionRepository.class); private static final String TABLE_NAME = EnvConfig.getRequired("CHAT_TABLE_NAME"); + private static final String GSI1_INDEX_NAME = "GSI1"; private final DynamoDbTable table; + private final DynamoDbIndex gsi1Index; public WordChainSessionRepository() { this.table = AwsClients.dynamoDbEnhanced() .table(TABLE_NAME, TableSchema.fromBean(WordChainSession.class)); + this.gsi1Index = table.index(GSI1_INDEX_NAME); } public WordChainSessionRepository(DynamoDbTable table) { this.table = table; + this.gsi1Index = table.index(GSI1_INDEX_NAME); } /** @@ -52,16 +57,16 @@ public Optional findById(String sessionId) { } /** - * 방의 활성 세션 조회 + * 방의 활성 세션 조회 (GSI1 인덱스 사용) */ public Optional findActiveByRoomId(String roomId) { - return table.query(QueryConditional.sortBeginsWith( + return gsi1Index.query(QueryConditional.sortBeginsWith( Key.builder() .partitionValue("ROOM#" + roomId) .sortValue("WORDCHAIN#") .build())) - .items() .stream() + .flatMap(page -> page.items().stream()) .filter(WordChainSession::isActive) .findFirst(); } diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java index 0eb3b1a2..cd7335d4 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/handler/OPIcSessionHandler.java @@ -11,7 +11,9 @@ import com.mzc.secondproject.serverless.common.util.ResponseGenerator; import com.mzc.secondproject.serverless.domain.opic.dto.request.CreateSessionRequest; import com.mzc.secondproject.serverless.domain.opic.dto.request.SubmitAnswerRequest; +import com.mzc.secondproject.serverless.domain.opic.dto.response.CreateSessionResponse; import com.mzc.secondproject.serverless.domain.opic.dto.response.FeedbackResponse; +import com.mzc.secondproject.serverless.domain.opic.dto.response.QuestionResponse; import com.mzc.secondproject.serverless.domain.opic.model.OPIcAnswer; import com.mzc.secondproject.serverless.domain.opic.model.OPIcQuestion; import com.mzc.secondproject.serverless.domain.opic.model.OPIcSession; @@ -119,55 +121,59 @@ public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent ev */ private APIGatewayProxyResponseEvent createSession(APIGatewayProxyRequestEvent event, String userId) { CreateSessionRequest request = gson.fromJson(event.getBody(), CreateSessionRequest.class); - - logger.info("세션 생성 요청: userId={}, topic={}, level={}", - userId, request.topic(), request.targetLevel()); - - // 주제 + 소주제 + 레벨로 질문 세트 조회 - List questions = repository.findQuestionsByTopicSubTopicAndLevel( - request.topic(), - request.subTopic(), - request.targetLevel() + + logger.info("세션 생성 요청: userId={}, topic={}, subTopic={}, targetLevel={}", + userId, request.topic(), request.subTopic(), request.targetLevel()); + + // 질문 세트 조회 (주제+소주제로 조회) + List questions = repository.findQuestionsByTopicAndSubTopic( + request.topic(), // 예: "DESCRIPTION" + request.subTopic() // 예: "HOMES" ); - + + // 질문 데이터 없음 예외 처리 if (questions.isEmpty()) { - return ResponseGenerator.notFound("해당 주제/레벨의 질문이 없습니다."); + String msg = String.format("해당 주제(%s)와 소주제(%s)에 해당하는 질문이 없습니다.", + request.topic(), request.subTopic()); + return ResponseGenerator.notFound(msg); } - - // 최대 3개 질문 선택 (랜덤 셔플) + + // 질문 3개 랜덤 선택 Collections.shuffle(questions); - List questionIds = questions.stream() + List selectedQuestions = questions.stream() .limit(3) + .sorted(Comparator.comparingInt(OPIcQuestion::getOrderInSet)) + .collect(Collectors.toList()); + + // 질문 ID 목록 추출 + List questionIds = selectedQuestions.stream() .map(OPIcQuestion::getQuestionId) .collect(Collectors.toList()); - - // 세션 생성 + + // 세션 저장 OPIcSession session = repository.createSession( userId, request.topic(), request.subTopic(), - request.targetLevel(), + request.targetLevel(), // 사용자가 선택한 레벨 저장 (AL, IM2 등) questionIds ); - - // 첫 질문 Polly 음성 URL 생성 (#368 PollyService 연동) - OPIcQuestion firstQuestion = questions.get(0); + + // 첫 번째 질문 응답 생성 + OPIcQuestion firstQuestion = selectedQuestions.get(0); String audioUrl = generateQuestionAudioUrl(firstQuestion); - - // Response - Map response = new LinkedHashMap<>(); - response.put("sessionId", session.getSessionId()); - response.put("totalQuestions", session.getTotalQuestions()); - response.put("firstQuestion", Map.of( - "questionId", firstQuestion.getQuestionId(), - "questionText", firstQuestion.getQuestionText(), - "audioUrl", audioUrl, - "questionNumber", 1, - "totalQuestions", session.getTotalQuestions() - )); - - logger.info("세션 생성 완료: sessionId={}", session.getSessionId()); - return ResponseGenerator.created("세션이 생성되었습니다.", response); + + QuestionResponse questionResponse = new QuestionResponse( + firstQuestion.getQuestionId(), + firstQuestion.getQuestionText(), + audioUrl, + 1, // 현재 질문 번호 + 3 // 총 질문 수 + ); + + return ResponseGenerator.ok( + new CreateSessionResponse(session.getSessionId(), questionResponse, 3) + ); } /** diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/model/OPIcQuestion.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/model/OPIcQuestion.java index 6a2056b1..0a85b9d4 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/model/OPIcQuestion.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/model/OPIcQuestion.java @@ -10,12 +10,12 @@ public class OPIcQuestion { private String pk; // QUESTION#questionId private String sk; // METADATA - private String gsi1pk; // TOPIC#travel - private String gsi1sk; // LEVEL#IM2 + private String gsi1pk; // TOPIC#DESCRIPTION (질문 유형 - 대주제) + private String gsi1sk; // SUBTOPIC#HOMES (질문 소재 - 소주제) private String questionId; - private String topic; // 대주제 - private String subTopic; // 소주제 + private String topic; // DESCRIPTION, HABIT, PAST_EXPERIENCE ... + private String subTopic; // HOMES, BANKS, MUSIC ... private String level; // 난이도 (IM1, IM2, IM3, IH, AL) private String questionText; // 질문 텍스트 (영어) private String questionTextKo; // 질문 텍스트 (한국어, 참고용) diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/repository/OPIcRepository.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/repository/OPIcRepository.java index 62251d18..3b53f323 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/repository/OPIcRepository.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/opic/repository/OPIcRepository.java @@ -151,20 +151,23 @@ public Optional findQuestionById(String questionId) { return Optional.ofNullable(questionTable.getItem(key)); } - + /** - * 주제 + 레벨로 질문 조회 (GSI1) + * 질문 유형 + 질문 주제로 조회 */ - public List findQuestionsByTopicAndLevel(String topic, String level) { + public List findQuestionsByTopicAndSubTopic(String topic, String subTopic) { DynamoDbIndex gsi1 = questionTable.index("GSI1"); - - QueryConditional queryConditional = QueryConditional.keyEqualTo( + + String pkVal = "TOPIC#" + topic.toUpperCase(); + String skVal = "SUBTOPIC#" + subTopic.toUpperCase(); + + QueryConditional queryConditional = QueryConditional.sortBeginsWith( Key.builder() - .partitionValue("TOPIC#" + topic) - .sortValue("LEVEL#" + level) + .partitionValue(pkVal) + .sortValue(skVal) .build() ); - + return gsi1.query(queryConditional) .stream() .flatMap(page -> page.items().stream()) @@ -172,17 +175,6 @@ public List findQuestionsByTopicAndLevel(String topic, String leve .collect(Collectors.toList()); } - /** - * 주제 + 소주제 + 레벨로 질문 조회 (subTopic 필터 추가) - */ - public List findQuestionsByTopicSubTopicAndLevel( - String topic, String subTopic, String level) { - - return findQuestionsByTopicAndLevel(topic, level).stream() - .filter(q -> subTopic == null || subTopic.equals(q.getSubTopic())) - .collect(Collectors.toList()); - } - /** * 여러 질문 ID로 조회 */ diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index 225dc206..385d914e 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -30,6 +30,7 @@ Globals: Architectures: - x86_64 Tracing: Active + AutoPublishAlias: live Environment: Variables: USER_TABLE_NAME: !Ref UserTable @@ -142,10 +143,10 @@ 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}" Identity: Header: Authorization @@ -402,7 +403,7 @@ Resources: Path: /chat/rooms Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetRooms: Type: Api Properties: @@ -410,7 +411,7 @@ Resources: Path: /chat/rooms Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetRoom: Type: Api Properties: @@ -418,7 +419,7 @@ Resources: Path: /chat/rooms/{roomId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DeleteRoom: Type: Api Properties: @@ -426,7 +427,7 @@ Resources: Path: /chat/rooms/{roomId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 JoinRoom: Type: Api Properties: @@ -434,7 +435,7 @@ Resources: Path: /chat/rooms/{roomId}/join Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 LeaveRoom: Type: Api Properties: @@ -442,7 +443,7 @@ Resources: Path: /chat/rooms/{roomId}/leave Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GameFunction: Type: AWS::Serverless::Function @@ -489,7 +490,7 @@ Resources: Path: /chat/rooms/{roomId}/game/start Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 StopGame: Type: Api Properties: @@ -497,7 +498,7 @@ Resources: Path: /chat/rooms/{roomId}/game/stop Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetGameStatus: Type: Api Properties: @@ -505,7 +506,7 @@ Resources: Path: /chat/rooms/{roomId}/game/status Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetScores: Type: Api Properties: @@ -513,7 +514,7 @@ Resources: Path: /chat/rooms/{roomId}/game/scores Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 끝말잇기(Word Chain) 게임 핸들러 WordChainFunction: @@ -546,7 +547,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/start Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 SubmitWord: Type: Api Properties: @@ -554,7 +555,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/submit Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 HandleTimeout: Type: Api Properties: @@ -562,7 +563,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/timeout Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 StopWordChain: Type: Api Properties: @@ -570,7 +571,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/stop Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetWordChainStatus: Type: Api Properties: @@ -578,7 +579,7 @@ Resources: Path: /chat/rooms/{roomId}/wordchain/status Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 게임 자동 종료 Lambda (EventBridge Scheduler에 의해 호출) GameAutoCloseFunction: @@ -666,7 +667,7 @@ Resources: Path: /chat/rooms/{roomId}/messages Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetMessages: Type: Api Properties: @@ -674,7 +675,7 @@ Resources: Path: /chat/rooms/{roomId}/messages Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetMessage: Type: Api Properties: @@ -682,7 +683,7 @@ Resources: Path: /chat/rooms/{roomId}/messages/{messageId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ChatVoiceFunction: Type: AWS::Serverless::Function @@ -712,7 +713,7 @@ Resources: Path: /chat/voice/synthesize Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Vocabulary Lambda Functions @@ -816,7 +817,7 @@ Resources: Path: /vocab/wrong-answers Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetUserWords: Type: Api Properties: @@ -824,7 +825,7 @@ Resources: Path: /vocab/user-words Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetUserWord: Type: Api Properties: @@ -832,7 +833,7 @@ Resources: Path: /vocab/user-words/{wordId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateUserWord: Type: Api Properties: @@ -840,7 +841,7 @@ Resources: Path: /vocab/user-words/{wordId} Method: PUT Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateUserWordTag: Type: Api Properties: @@ -848,7 +849,7 @@ Resources: Path: /vocab/user-words/{wordId}/tag Method: PATCH Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateUserWordStatus: Type: Api Properties: @@ -856,7 +857,7 @@ Resources: Path: /vocab/user-words/{wordId}/status Method: PATCH Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 WordGroupFunction: Type: AWS::Serverless::Function @@ -878,7 +879,7 @@ Resources: Path: /vocab/groups Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetGroups: Type: Api Properties: @@ -886,7 +887,7 @@ Resources: Path: /vocab/groups Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetGroupDetail: Type: Api Properties: @@ -894,7 +895,7 @@ Resources: Path: /vocab/groups/{groupId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 UpdateGroup: Type: Api Properties: @@ -902,7 +903,7 @@ Resources: Path: /vocab/groups/{groupId} Method: PUT Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DeleteGroup: Type: Api Properties: @@ -910,7 +911,7 @@ Resources: Path: /vocab/groups/{groupId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 AddWordToGroup: Type: Api Properties: @@ -918,7 +919,7 @@ Resources: Path: /vocab/groups/{groupId}/words/{wordId} Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 RemoveWordFromGroup: Type: Api Properties: @@ -926,7 +927,7 @@ Resources: Path: /vocab/groups/{groupId}/words/{wordId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DailyStudyFunction: Type: AWS::Serverless::Function @@ -953,7 +954,7 @@ Resources: Path: /vocab/daily Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 MarkWordLearned: Type: Api Properties: @@ -961,7 +962,7 @@ Resources: Path: /vocab/daily/words/{wordId}/learned Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 TestFunction: Type: AWS::Serverless::Function @@ -991,7 +992,7 @@ Resources: Path: /vocab/test/start Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 SubmitAnswer: Type: Api Properties: @@ -999,7 +1000,7 @@ Resources: Path: /vocab/test/submit Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTestResults: Type: Api Properties: @@ -1007,7 +1008,7 @@ Resources: Path: /vocab/test/results Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTestResultDetail: Type: Api Properties: @@ -1015,7 +1016,7 @@ Resources: Path: /vocab/test/results/{testId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTestedWords: Type: Api Properties: @@ -1023,7 +1024,7 @@ Resources: Path: /vocab/test/tested-words Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 StatsFunction: Type: AWS::Serverless::Function @@ -1045,7 +1046,7 @@ Resources: Path: /vocab/stats Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetDailyStats: Type: Api Properties: @@ -1053,7 +1054,7 @@ Resources: Path: /vocab/stats/daily Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetWeaknessAnalysis: Type: Api Properties: @@ -1061,7 +1062,7 @@ Resources: Path: /vocab/stats/weakness Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 VocabVoiceFunction: Type: AWS::Serverless::Function @@ -1143,7 +1144,7 @@ Resources: Path: /stats/daily Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetWeeklyStats: Type: Api Properties: @@ -1151,7 +1152,7 @@ Resources: Path: /stats/weekly Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetMonthlyStats: Type: Api Properties: @@ -1159,7 +1160,7 @@ Resources: Path: /stats/monthly Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetTotalStats: Type: Api Properties: @@ -1167,7 +1168,7 @@ Resources: Path: /stats/total Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetDashboard: Type: Api Properties: @@ -1175,7 +1176,7 @@ Resources: Path: /stats/dashboard Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetStatsHistory: Type: Api Properties: @@ -1183,7 +1184,7 @@ Resources: Path: /stats/history Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # Badge Lambda Function BadgeFunction: @@ -1213,7 +1214,7 @@ Resources: Path: /badges Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetEarnedBadges: Type: Api Properties: @@ -1221,7 +1222,7 @@ Resources: Path: /badges/earned Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Grammar Lambda Functions @@ -1268,7 +1269,7 @@ Resources: Path: /grammar/check Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GrammarConversation: Type: Api Properties: @@ -1276,7 +1277,7 @@ Resources: Path: /grammar/conversation Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetSessions: Type: Api Properties: @@ -1284,7 +1285,7 @@ Resources: Path: /grammar/sessions Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 GetSessionDetail: Type: Api Properties: @@ -1292,7 +1293,7 @@ Resources: Path: /grammar/sessions/{sessionId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 DeleteSession: Type: Api Properties: @@ -1300,7 +1301,7 @@ Resources: Path: /grammar/sessions/{sessionId} Method: DELETE Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Grammar WebSocket API (Streaming) @@ -1564,7 +1565,7 @@ Resources: Path: /opic/sessions Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 세션 조회 GetSession: Type: Api @@ -1573,7 +1574,7 @@ Resources: Path: /opic/sessions/{sessionId} Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 세션 목록 조회 GetSessions: Type: Api @@ -1582,7 +1583,7 @@ Resources: Path: /opic/sessions Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 다음 질문 조회 GetNextQuestion: Type: Api @@ -1591,7 +1592,7 @@ Resources: Path: /opic/sessions/{sessionId}/questions/next Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 답변 제출 SubmitAnswer: Type: Api @@ -1600,7 +1601,7 @@ Resources: Path: /opic/sessions/{sessionId}/answers Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 세션 완료 CompleteSession: Type: Api @@ -1609,7 +1610,7 @@ Resources: Path: /opic/sessions/{sessionId}/complete Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 # 음성 업로드 Presigned URL GetUploadUrl: Type: Api @@ -1618,7 +1619,7 @@ Resources: Path: /opic/sessions/{sessionId}/upload-url Method: GET Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # Speaking Lambda Functions @@ -1660,7 +1661,7 @@ Resources: Path: /speaking/chat Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 SpeakingReset: Type: Api Properties: @@ -1668,7 +1669,7 @@ Resources: Path: /speaking/reset Method: POST Auth: - Authorizer: CognitoAuthorizer + Authorizer: CognitoAuthV2 ############################################# # DynamoDB Tables diff --git a/seed/opic/question-homes.json b/seed/opic/question-homes.json deleted file mode 100644 index ddd67703..00000000 --- a/seed/opic/question-homes.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "questions": [ - { - "questionId": "desc-homes-001", - "topic": "DESCRIPTION", - "subTopic": "HOMES", - "questionText": "I would like to know about where you live. Talk about the different rooms at your place. Tell me about your favorite room in your home. What does it look like?", - "difficulty": "IM2", - "questionNumber": 44 - }, - { - "questionId": "desc-homes-002", - "topic": "DESCRIPTION", - "subTopic": "HOMES", - "questionText": "I would like to know where you live. Can you describe your home to me? What does it look like? How many rooms does it have? Give me a description with lots of details.", - "difficulty": "IM2", - "questionNumber": 45 - }, - { - "questionId": "desc-homes-003", - "topic": "DESCRIPTION", - "subTopic": "HOMES", - "questionText": "Now, let's talk about your bedroom. What's inside? What kind of furniture do you have in your room?", - "difficulty": "IM1", - "questionNumber": 46 - }, - { - "questionId": "habit-homes-001", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "What kinds of home improvement projects do you enjoy doing and why?", - "difficulty": "IM2", - "questionNumber": 139 - }, - { - "questionId": "habit-homes-002", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "Tell me about your normal routine at home. What do you do on weekdays and on weekends?", - "difficulty": "IM1", - "questionNumber": 140 - }, - { - "questionId": "habit-homes-003", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "What is your normal routine at home? What things do you usually do on weekdays and on weekends?", - "difficulty": "IM1", - "questionNumber": 141 - }, - { - "questionId": "habit-homes-004", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "What is your responsibility at home? What is your role? Tell me in detail.", - "difficulty": "IM2", - "questionNumber": 142 - }, - { - "questionId": "habit-homes-005", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "What kinds of things do you do to keep your house clean and comfortable? What kinds of housework do you do at home?", - "difficulty": "IM1", - "questionNumber": 143 - }, - { - "questionId": "habit-homes-006", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "What kind of house work do you usually do at home? Do you share your chores with your family?", - "difficulty": "IM1", - "questionNumber": 144 - }, - { - "questionId": "habit-homes-007", - "topic": "HABIT", - "subTopic": "HOMES", - "questionText": "Tell me about all the steps you take for a home improvement project. What steps do you usually take? How do you complete the project?", - "difficulty": "IH", - "questionNumber": 145 - }, - { - "questionId": "past-homes-001", - "topic": "PAST_EXPERIENCE", - "subTopic": "HOMES", - "questionText": "Tell me about a memorable experience you had at home. What happened and why was it memorable?", - "difficulty": "IM2", - "questionNumber": 0 - }, - { - "questionId": "past-homes-002", - "topic": "PAST_EXPERIENCE", - "subTopic": "HOMES", - "questionText": "Have you ever had any problems at home? Maybe something broke or there was an issue with your neighbors. Tell me about that experience and how you solved it.", - "difficulty": "IH", - "questionNumber": 0 - }, - { - "questionId": "past-homes-003", - "topic": "PAST_EXPERIENCE", - "subTopic": "HOMES", - "questionText": "Tell me about a time when you had to do a major cleaning or organizing at home. What did you do and how did it turn out?", - "difficulty": "IM2", - "questionNumber": 0 - } - ] -}