From 170d56c650a6dcf8a5d9335e1eda4b01b6ab616e Mon Sep 17 00:00:00 2001 From: vayaconchoi Date: Fri, 1 Aug 2025 13:09:49 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix(=ED=9C=B4=EB=8C=80=EC=A0=84=ED=99=94?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80):=20=ED=9C=B4=EB=8C=80=EC=A0=84=ED=99=94?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20DP-180?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 휴대전화번호 검증 로직 추가 - 휴대전화가 사용되는 함수(인증번호 발송, 회원가입, 아이디/비밀번호 찾기)에 휴대전화 포멧 검증 로직 추가 --- .../deepwebide_be/global/exception/ErrorCode.java | 1 + .../member/service/PhoneVerificationService.java | 8 +++++++- .../deepwebide_be/member/service/UserService.java | 5 ++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/deepdirect/deepwebide_be/global/exception/ErrorCode.java b/src/main/java/com/deepdirect/deepwebide_be/global/exception/ErrorCode.java index 0d70abcd..76f60248 100644 --- a/src/main/java/com/deepdirect/deepwebide_be/global/exception/ErrorCode.java +++ b/src/main/java/com/deepdirect/deepwebide_be/global/exception/ErrorCode.java @@ -15,6 +15,7 @@ public enum ErrorCode { REPOSITORY_MEMBER_LIMIT_EXCEEDED(HttpStatus.BAD_REQUEST, "최대 인원이 초과되어 입장할 수 없습니다."), INVALID_USERNAME(HttpStatus.BAD_REQUEST, "이름은 한글 2자 이상만 입력 가능합니다."), INVALID_PASSWORD_FORMAT(HttpStatus.BAD_REQUEST, "비밀번호는 영어 대문자, 소문자, 숫자, 특수문자를 모두 포함해야 합니다."), + INVALID_PHONE_FORMAT(HttpStatus.BAD_REQUEST, "올바른 휴대전화 번호 형식이 아닙니다."), VERIFICATION_CODE_EXPIRED(HttpStatus.BAD_REQUEST, "인증번호가 만료되었습니다."), NOT_OWNER_CHANGE(HttpStatus.BAD_REQUEST, "오너만 이름을 변경할 수 있습니다."), //오너만 이름 변경!! NOT_OWNER_DELETE(HttpStatus.BAD_REQUEST, "오너만 삭제할 수 있습니다."), //오너만 삭제 가능 diff --git a/src/main/java/com/deepdirect/deepwebide_be/member/service/PhoneVerificationService.java b/src/main/java/com/deepdirect/deepwebide_be/member/service/PhoneVerificationService.java index 84bbf64e..02ea55ed 100644 --- a/src/main/java/com/deepdirect/deepwebide_be/member/service/PhoneVerificationService.java +++ b/src/main/java/com/deepdirect/deepwebide_be/member/service/PhoneVerificationService.java @@ -16,6 +16,7 @@ import java.time.LocalDateTime; import java.util.Random; +import java.util.regex.Pattern; @Service @RequiredArgsConstructor @@ -55,7 +56,13 @@ private String generateRandomNumber() { } // 문자 발송 + public int sendVerificationCode(String phoneNumber, String reqUserName, AuthType reqAuthType) { + // 휴대전화 번호 형식 정규식 (010으로 시작하고 숫자 총 10~11자리) + Pattern phonePattern = Pattern.compile("^01[0|1|6|7|8|9][0-9]{7,8}$"); + if (!phonePattern.matcher(phoneNumber).matches()) { + throw new GlobalException(ErrorCode.INVALID_PHONE_FORMAT); + } if (reqAuthType == AuthType.SIGN_UP && isUserExists(phoneNumber, reqUserName)) { throw new GlobalException(ErrorCode.DUPLICATE_NAME_AND_PHONE); @@ -74,7 +81,6 @@ public int sendVerificationCode(String phoneNumber, String reqUserName, AuthType throw new GlobalException(ErrorCode.SMS_SEND_FAILED); } - // 인증 정보 저장 phoneVerificationRepository.save( PhoneVerification.builder() .phoneNumber(phoneNumber) diff --git a/src/main/java/com/deepdirect/deepwebide_be/member/service/UserService.java b/src/main/java/com/deepdirect/deepwebide_be/member/service/UserService.java index 82d7b207..301e31f3 100644 --- a/src/main/java/com/deepdirect/deepwebide_be/member/service/UserService.java +++ b/src/main/java/com/deepdirect/deepwebide_be/member/service/UserService.java @@ -82,7 +82,10 @@ public SignUpResponse signup(SignUpRequest request) { if (userRepository.existsByEmail(request.getEmail())) { throw new GlobalException(ErrorCode.DUPLICATE_EMAIL); } - + Pattern phonePattern = Pattern.compile("^01[0|1|6|7|8|9][0-9]{7,8}$"); + if (!phonePattern.matcher(request.getPhoneNumber()).matches()) { + throw new GlobalException(ErrorCode.INVALID_PHONE_FORMAT); + } if (userRepository.existsByPhoneNumber(request.getPhoneNumber())) { throw new GlobalException(ErrorCode.PHONE_NUMBER_ALREADY_USED); } From e2751ce2f5129284b1ddc67212db58ea7f43a23a Mon Sep 17 00:00:00 2001 From: vayaconchoi Date: Fri, 1 Aug 2025 13:14:43 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix(=EC=B6=A9=EB=8F=8C=20=EC=A0=9C=EC=96=B4?= =?UTF-8?q?):=20=EC=B6=A9=EB=8F=8C=20=EC=A0=9C=EC=96=B4=20DP-180?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deepdirect/deepwebide_be/global/exception/ErrorCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/deepdirect/deepwebide_be/global/exception/ErrorCode.java b/src/main/java/com/deepdirect/deepwebide_be/global/exception/ErrorCode.java index 76f60248..eecc04a7 100644 --- a/src/main/java/com/deepdirect/deepwebide_be/global/exception/ErrorCode.java +++ b/src/main/java/com/deepdirect/deepwebide_be/global/exception/ErrorCode.java @@ -15,7 +15,6 @@ public enum ErrorCode { REPOSITORY_MEMBER_LIMIT_EXCEEDED(HttpStatus.BAD_REQUEST, "최대 인원이 초과되어 입장할 수 없습니다."), INVALID_USERNAME(HttpStatus.BAD_REQUEST, "이름은 한글 2자 이상만 입력 가능합니다."), INVALID_PASSWORD_FORMAT(HttpStatus.BAD_REQUEST, "비밀번호는 영어 대문자, 소문자, 숫자, 특수문자를 모두 포함해야 합니다."), - INVALID_PHONE_FORMAT(HttpStatus.BAD_REQUEST, "올바른 휴대전화 번호 형식이 아닙니다."), VERIFICATION_CODE_EXPIRED(HttpStatus.BAD_REQUEST, "인증번호가 만료되었습니다."), NOT_OWNER_CHANGE(HttpStatus.BAD_REQUEST, "오너만 이름을 변경할 수 있습니다."), //오너만 이름 변경!! NOT_OWNER_DELETE(HttpStatus.BAD_REQUEST, "오너만 삭제할 수 있습니다."), //오너만 삭제 가능 @@ -42,6 +41,7 @@ public enum ErrorCode { UNSUPPORTED_REPOSITORY_TYPE(HttpStatus.BAD_REQUEST, "지원하지 않는 레포지토리 타입입니다."), REPOSITORY_FILES_NOT_FOUND(HttpStatus.BAD_REQUEST, "레포지토리 파일을 찾을 수 없습니다."), FILE_TREE_CONVERSION_FAILED(HttpStatus.BAD_REQUEST, "파일 트리 변환에 실패했습니다."), + INVALID_PHONE_FORMAT(HttpStatus.BAD_REQUEST, "올바른 휴대전화 번호 형식이 아닙니다."), // 401 UNAUTHORIZED UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "인증되지 않았습니다."), From 5ed38a3fbc259df44a2e1202eb450be07ea23f82 Mon Sep 17 00:00:00 2001 From: sunsetkk Date: Fri, 1 Aug 2025 18:05:25 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix(chat)=20=EC=B1=84=ED=8C=85=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=9A=94=EC=B2=AD/=EC=9D=91=EB=8B=B5=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=AA=85=EC=84=B8=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ChatWebSocketController.java | 7 +++-- .../chat/domain/ChatMessageReference.java | 6 ++--- .../chat/dto/request/ChatMessageRequest.java | 10 ++++++- .../dto/request/CodeReferenceRequest.java | 16 ++++++++++++ .../dto/response/ChatMessageBroadcast.java | 9 +++++-- .../chat/service/ChatMessageWriteService.java | 26 ++++++++++++++++--- 6 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/deepdirect/deepwebide_be/chat/dto/request/CodeReferenceRequest.java diff --git a/src/main/java/com/deepdirect/deepwebide_be/chat/controller/ChatWebSocketController.java b/src/main/java/com/deepdirect/deepwebide_be/chat/controller/ChatWebSocketController.java index a3ee0017..852fe647 100644 --- a/src/main/java/com/deepdirect/deepwebide_be/chat/controller/ChatWebSocketController.java +++ b/src/main/java/com/deepdirect/deepwebide_be/chat/controller/ChatWebSocketController.java @@ -5,7 +5,6 @@ import com.deepdirect.deepwebide_be.chat.service.ChatMessageWriteService; import com.deepdirect.deepwebide_be.chat.util.RedisPublisher; import lombok.RequiredArgsConstructor; -import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.stereotype.Controller; @@ -19,17 +18,17 @@ public class ChatWebSocketController { @MessageMapping("/repositories/{repositoryId}/chat/send") public void sendMessage( - @DestinationVariable Long repositoryId, ChatMessageRequest request, SimpMessageHeaderAccessor headerAccessor ) { + // 1. WebSocket 세션에서 userId 추출 Long userId = (Long) headerAccessor.getSessionAttributes().get("userId"); // 2. 메시지 저장 + DTO 응답 변환 - ChatMessageBroadcast broadcast = chatMessageWriteService.saveChatMessage(userId, repositoryId, request.getMessage()); + ChatMessageBroadcast broadcast = chatMessageWriteService.saveChatMessage(userId, request); // 3. Redis 채널로 publish - redisPublisher.publish("chatroom:" + repositoryId, broadcast); + redisPublisher.publish("chatroom:" + request.getRepositoryId(), broadcast); } } \ No newline at end of file diff --git a/src/main/java/com/deepdirect/deepwebide_be/chat/domain/ChatMessageReference.java b/src/main/java/com/deepdirect/deepwebide_be/chat/domain/ChatMessageReference.java index 7a4aad1e..a1411737 100644 --- a/src/main/java/com/deepdirect/deepwebide_be/chat/domain/ChatMessageReference.java +++ b/src/main/java/com/deepdirect/deepwebide_be/chat/domain/ChatMessageReference.java @@ -1,9 +1,7 @@ package com.deepdirect.deepwebide_be.chat.domain; import jakarta.persistence.*; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import java.time.LocalDateTime; @@ -11,6 +9,8 @@ @Table(name = "chat_message_references") @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Builder public class ChatMessageReference { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/com/deepdirect/deepwebide_be/chat/dto/request/ChatMessageRequest.java b/src/main/java/com/deepdirect/deepwebide_be/chat/dto/request/ChatMessageRequest.java index b7ecd625..a560b0a8 100644 --- a/src/main/java/com/deepdirect/deepwebide_be/chat/dto/request/ChatMessageRequest.java +++ b/src/main/java/com/deepdirect/deepwebide_be/chat/dto/request/ChatMessageRequest.java @@ -21,9 +21,17 @@ public class ChatMessageRequest { @JsonProperty("type") private ChatMessageType type; + @Schema(description = "레포지토리 ID") + private Long repositoryId; + + @Schema(description = "코드 참조 정보") + private CodeReferenceRequest codeReference; + @Builder - public ChatMessageRequest(String message, ChatMessageType type) { + public ChatMessageRequest(String message, ChatMessageType type, Long repositoryId, CodeReferenceRequest codeReference) { this.message = message; this.type = type; + this.repositoryId = repositoryId; + this.codeReference = codeReference; } } diff --git a/src/main/java/com/deepdirect/deepwebide_be/chat/dto/request/CodeReferenceRequest.java b/src/main/java/com/deepdirect/deepwebide_be/chat/dto/request/CodeReferenceRequest.java new file mode 100644 index 00000000..f90fa0da --- /dev/null +++ b/src/main/java/com/deepdirect/deepwebide_be/chat/dto/request/CodeReferenceRequest.java @@ -0,0 +1,16 @@ +package com.deepdirect.deepwebide_be.chat.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "코드 참조 요청 정보") +public class CodeReferenceRequest { + + private Long referenceId; + private String path; +} diff --git a/src/main/java/com/deepdirect/deepwebide_be/chat/dto/response/ChatMessageBroadcast.java b/src/main/java/com/deepdirect/deepwebide_be/chat/dto/response/ChatMessageBroadcast.java index d03eea82..05049e0e 100644 --- a/src/main/java/com/deepdirect/deepwebide_be/chat/dto/response/ChatMessageBroadcast.java +++ b/src/main/java/com/deepdirect/deepwebide_be/chat/dto/response/ChatMessageBroadcast.java @@ -35,6 +35,9 @@ public class ChatMessageBroadcast { @Schema(description = "메시지 내용") private String message; + @Schema(description = "코드 참조 정보", nullable = true) + private CodeReferenceResponse codeReference; + @Schema(description = "보낸 시간") private LocalDateTime sentAt; @@ -42,7 +45,7 @@ public class ChatMessageBroadcast { private boolean isMine; - public static ChatMessageBroadcast of(ChatMessage message, User sender, Long repositoryId) { + public static ChatMessageBroadcast of(ChatMessage message, User sender, Long repositoryId, CodeReferenceResponse codeReference) { return ChatMessageBroadcast.builder() .repositoryId(repositoryId) .type(ChatMessageType.CHAT) //일반 채팅 @@ -51,8 +54,9 @@ public static ChatMessageBroadcast of(ChatMessage message, User sender, Long rep .senderNickname(sender.getNickname()) .senderProfileImageUrl(sender.getProfileImageUrl()) .message(message.getMessage()) + .codeReference(codeReference) .sentAt(message.getSentAt()) - .isMine(true) + .isMine(false) .build(); } @@ -65,6 +69,7 @@ public static ChatMessageBroadcast system(User user, ChatMessageType type, Strin .senderNickname(user.getNickname()) .senderProfileImageUrl(user.getProfileImageUrl()) .message(message) + .codeReference(null) .sentAt(LocalDateTime.now()) .isMine(false) .build(); diff --git a/src/main/java/com/deepdirect/deepwebide_be/chat/service/ChatMessageWriteService.java b/src/main/java/com/deepdirect/deepwebide_be/chat/service/ChatMessageWriteService.java index a475c333..1710e41f 100644 --- a/src/main/java/com/deepdirect/deepwebide_be/chat/service/ChatMessageWriteService.java +++ b/src/main/java/com/deepdirect/deepwebide_be/chat/service/ChatMessageWriteService.java @@ -2,7 +2,10 @@ import com.deepdirect.deepwebide_be.chat.domain.ChatMessage; import com.deepdirect.deepwebide_be.chat.domain.ChatMessageReference; +import com.deepdirect.deepwebide_be.chat.dto.request.ChatMessageRequest; +import com.deepdirect.deepwebide_be.chat.dto.request.CodeReferenceRequest; import com.deepdirect.deepwebide_be.chat.dto.response.ChatMessageBroadcast; +import com.deepdirect.deepwebide_be.chat.dto.response.CodeReferenceResponse; import com.deepdirect.deepwebide_be.chat.repository.ChatMessageReferenceRepository; import com.deepdirect.deepwebide_be.chat.repository.ChatMessageRepository; import com.deepdirect.deepwebide_be.global.exception.GlobalException; @@ -30,7 +33,9 @@ public class ChatMessageWriteService { private final ChatMessageReferenceRepository referenceRepository; @Transactional - public ChatMessageBroadcast saveChatMessage(Long userId, Long repositoryId, String content) { + public ChatMessageBroadcast saveChatMessage(Long userId, ChatMessageRequest request) { + Long repositoryId = request.getRepositoryId(); + Repository repository = repositoryRepository.findByIdAndDeletedAtIsNull(repositoryId) .orElseThrow(() -> new GlobalException(REPOSITORY_NOT_FOUND)); @@ -45,14 +50,29 @@ public ChatMessageBroadcast saveChatMessage(Long userId, Long repositoryId, Stri User sender = userRepository.findById(userId) .orElseThrow(() -> new GlobalException(USER_NOT_FOUND)); + String content = request.getMessage(); if (content == null || content.isEmpty()) { throw new GlobalException(EMPTY_CHAT_MESSAGE); } + // 1. 메시지 저장 ChatMessage chatMessage = chatMessageRepository.save( ChatMessage.of(repository, sender, content) ); - return ChatMessageBroadcast.of(chatMessage, sender, repositoryId); + // 2. 참조 저장 (있으면) + CodeReferenceRequest refRequest = request.getCodeReference(); + ChatMessageReference reference = null; + if (refRequest != null && refRequest.getPath() != null) { + reference = referenceRepository.save(ChatMessageReference.builder() + .chatMessage(chatMessage) + .path(refRequest.getPath()) + .build()); + } + + // 3. 응답 DTO 변환 + CodeReferenceResponse codeReferenceResponse = reference != null ? CodeReferenceResponse.from(reference) : null; + + return ChatMessageBroadcast.of(chatMessage, sender, repositoryId, codeReferenceResponse); } -} \ No newline at end of file +}