Skip to content

Commit 65fb40e

Browse files
committed
feat: Spring Validation 추가 및 유효성 검사 구현
- 주요 Request DTO에 제약 조건 어노테이션 추가 - @Valid를 사용해 컨트롤러에서 요청 데이터 검증 - 유효성 검증 실패 시 MethodArgumentNotValidException 전역 예외 처리 및 ErrorResponse 반환
1 parent ee448c4 commit 65fb40e

17 files changed

+139
-82
lines changed

src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.sprint.mission.discodeit.dto.channel.*;
55
import com.sprint.mission.discodeit.entity.Channel;
66
import com.sprint.mission.discodeit.service.ChannelService;
7+
import jakarta.validation.Valid;
78
import lombok.RequiredArgsConstructor;
89
import lombok.extern.slf4j.Slf4j;
910
import org.springframework.http.HttpStatus;
@@ -23,7 +24,7 @@ public class ChannelController {
2324

2425
@PostMapping(value = "/public")
2526
public ResponseEntity<ChannelDto> createPublicChannel(
26-
@RequestBody ChannelPublicRequest channelPublicRequest) {
27+
@Valid @RequestBody ChannelPublicRequest channelPublicRequest) {
2728
log.info("공개 채널 생성 요청(Request): publicChannelName={}", channelPublicRequest.name());
2829

2930
log.info("공개 채널 생성 응답(Response): publicChannelName={}, HttpStatus={}",
@@ -51,7 +52,7 @@ public ResponseEntity<ChannelDto> createPrivateChannel(
5152
// 무엇보다 Public 채널을 업데이트하는 건데 엔드 포인트에 public이 붙지 않는 것도 고민되는 부분입니다.
5253
@PatchMapping(value = "/{channelId}")
5354
public ResponseEntity<ChannelDto> updatePublicChannel(@PathVariable UUID channelId,
54-
@RequestBody ChannelUpdateRequest channelUpdateRequest) {
55+
@Valid @RequestBody ChannelUpdateRequest channelUpdateRequest) {
5556
log.info("채널 수정 요청(Request): nameChanged={}, descriptionChanged={}",
5657
channelUpdateRequest.newName() != null,
5758
channelUpdateRequest.newDescription() != null

src/main/java/com/sprint/mission/discodeit/controller/MessageController.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.sprint.mission.discodeit.dto.reponse.PageResponse;
99
import com.sprint.mission.discodeit.entity.Message;
1010
import com.sprint.mission.discodeit.service.MessageService;
11+
import jakarta.validation.Valid;
1112
import java.util.ArrayList;
1213
import lombok.RequiredArgsConstructor;
1314
import lombok.extern.slf4j.Slf4j;
@@ -34,16 +35,16 @@ public class MessageController {
3435

3536
@PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
3637
public ResponseEntity<MessageDto> createMessage(
37-
@RequestPart(value = "messageCreateRequest") MessageCreateRequest messageCreateRequest,
38+
@Valid @RequestPart(value = "messageCreateRequest") MessageCreateRequest messageCreateRequest,
3839
@RequestPart(value = "binaryContents", required = false) List<MultipartFile> attachments)
3940
throws Exception {
4041
log.info("메세지 생성 요청(Request): messageContent={}, hasProfileImage={}",
4142
messageCreateRequest.content(),
42-
!attachments.isEmpty());
43+
attachments != null);
4344

4445
// 메세지 첨부 파일 생성
4546
List<BinaryContentCreateRequest> binaryContentCreateRequests = new ArrayList<>();
46-
if (!attachments.isEmpty()) {
47+
if (attachments != null) {
4748
for (MultipartFile file : attachments) {
4849
log.debug("메세지 첨부 파일 생성: fileName={}", file.getName());
4950
binaryContentCreateRequests.add(new BinaryContentCreateRequest(file));
@@ -63,7 +64,7 @@ public ResponseEntity<MessageDto> createMessage(
6364

6465
@PatchMapping(value = "/{messageId}")
6566
public ResponseEntity<MessageDto> updateMessage(@PathVariable UUID messageId,
66-
@RequestBody MessageUpdateRequest messageUpdateRequest) {
67+
@Valid @RequestBody MessageUpdateRequest messageUpdateRequest) {
6768
log.info("메세지 수정 요청(Request): messageChanged={}", !messageUpdateRequest.newMessage().isEmpty());
6869

6970
// 메세지 수정

src/main/java/com/sprint/mission/discodeit/controller/ReadStatusController.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.sprint.mission.discodeit.dto.readStatus.*;
55
import com.sprint.mission.discodeit.entity.ReadStatus;
66
import com.sprint.mission.discodeit.service.ReadStatusService;
7+
import jakarta.validation.Valid;
78
import lombok.RequiredArgsConstructor;
89
import org.springframework.http.HttpStatus;
910
import org.springframework.http.ResponseEntity;
@@ -22,15 +23,15 @@ public class ReadStatusController {
2223

2324
@PostMapping
2425
public ResponseEntity<ReadStatusDto> createReadStatus(
25-
@RequestBody ReadStatusCreateRequest readStatusCreateRequest) {
26+
@Valid @RequestBody ReadStatusCreateRequest readStatusCreateRequest) {
2627
ReadStatusDto readStatusDto = readStatusService.createReadStatus(readStatusCreateRequest);
2728
return ResponseEntity.status(HttpStatus.CREATED).body(readStatusDto);
2829
}
2930

3031
@PatchMapping(value = "/{readStatusId}")
3132
public ResponseEntity<ReadStatusDto> updateReadStatus(
3233
@PathVariable UUID readStatusId,
33-
@RequestBody ReadStatusUpdateRequest readStatusUpdateRequest) {
34+
@Valid @RequestBody ReadStatusUpdateRequest readStatusUpdateRequest) {
3435
return ResponseEntity.ok(
3536
readStatusService.updateReadStatus(readStatusId, readStatusUpdateRequest));
3637
}

src/main/java/com/sprint/mission/discodeit/controller/UserController.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import com.sprint.mission.discodeit.dto.UserDto;
55
import com.sprint.mission.discodeit.dto.UserStatusDto;
66
import com.sprint.mission.discodeit.dto.user.*;
7+
import com.sprint.mission.discodeit.dto.userStatus.UserStatusUpdateByUserIdRequest;
78
import com.sprint.mission.discodeit.service.UserService;
89
import com.sprint.mission.discodeit.service.UserStatusService;
10+
import jakarta.validation.Valid;
911
import java.util.List;
1012
import lombok.RequiredArgsConstructor;
1113
import lombok.extern.slf4j.Slf4j;
@@ -28,7 +30,7 @@ public class UserController {
2830

2931
@PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
3032
public ResponseEntity<UserDto> createUser(
31-
@RequestPart(value = "userCreateRequest") UserCreateRequest userCreateRequest,
33+
@Valid @RequestPart(value = "userCreateRequest") UserCreateRequest userCreateRequest,
3234
@RequestPart(value = "binaryContent", required = false) MultipartFile file) throws Exception {
3335

3436
/* 유저 생성 요청(Request) */
@@ -73,7 +75,7 @@ public ResponseEntity<UserDto> createUser(
7375

7476
@PatchMapping(value = "/{userId}", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
7577
public ResponseEntity<UserDto> updateUser(@PathVariable UUID userId,
76-
@RequestPart(value = "userUpdateRequest") UserUpdateRequest userUpdateRequest,
78+
@Valid @RequestPart(value = "userUpdateRequest") UserUpdateRequest userUpdateRequest,
7779
@RequestPart(value = "profile", required = false) MultipartFile file) throws Exception {
7880
log.info(
7981
"유저 수정 요청(Request): usernameChanged={}, emailChanged={}, passwordChanged={}, hasProfileImage={}",

src/main/java/com/sprint/mission/discodeit/dto/channel/ChannelPublicRequest.java

+4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package com.sprint.mission.discodeit.dto.channel;
22

33
import com.sprint.mission.discodeit.entity.ChannelType;
4+
import jakarta.validation.constraints.NotBlank;
5+
import jakarta.validation.constraints.Size;
46
import java.util.UUID;
57

68
public record ChannelPublicRequest(
9+
@NotBlank(message = "채널 이름은 공백으로 둘 수 없습니다.")
10+
@Size(min = 2, max = 30, message = "channelName 길이는 2~30자여야 합니다.")
711
String name,
812
String description
913
) {
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
package com.sprint.mission.discodeit.dto.channel;
22

3-
public record ChannelUpdateRequest (
4-
String newName,
5-
String newDescription
6-
){
3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Size;
5+
6+
public record ChannelUpdateRequest(
7+
@NotBlank(message = "채널 이름은 공백으로 둘 수 없습니다.")
8+
@Size(min = 2, max = 30, message = "channelName 길이는 2~30자여야 합니다.")
9+
String newName,
10+
String newDescription
11+
) {
12+
713
}

src/main/java/com/sprint/mission/discodeit/dto/message/MessageCreateRequest.java

+5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@
22

33
import com.sprint.mission.discodeit.entity.Channel;
44

5+
import jakarta.validation.constraints.NotBlank;
6+
import jakarta.validation.constraints.NotNull;
57
import java.util.UUID;
68

79
public record MessageCreateRequest(
10+
@NotNull(message = "channelId 필드는 반드시 값이 필요합니다.")
811
UUID channelId,
12+
@NotNull(message = "authorId 필드는 반드시 값이 필요합니다.")
913
UUID authorId,
14+
@NotBlank(message = "message 는 공백일 수 없습니다.")
1015
String content
1116
) {
1217

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.sprint.mission.discodeit.dto.message;
22

3+
import jakarta.validation.constraints.NotBlank;
34
import java.util.UUID;
45

56
public record MessageUpdateRequest(
6-
String newMessage
7-
){
7+
@NotBlank(message = "message 는 공백일 수 없습니다.")
8+
String newMessage
9+
) {
10+
811
}

src/main/java/com/sprint/mission/discodeit/dto/readStatus/ReadStatusCreateRequest.java

+4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22

33
import com.sprint.mission.discodeit.entity.Channel;
44
import com.sprint.mission.discodeit.entity.User;
5+
import jakarta.validation.constraints.NotNull;
56
import java.time.Instant;
67

78
public record ReadStatusCreateRequest(
9+
@NotNull(message = "user 필드는 반드시 값이 필요합니다.")
810
User user,
11+
@NotNull(message = "channel 필드는 반드시 값이 필요합니다,")
912
Channel channel,
13+
@NotNull(message = "lastReadAt 필드는 반드시 값이 필요합니다.")
1014
Instant lastReadAt
1115
) {
1216

src/main/java/com/sprint/mission/discodeit/dto/readStatus/ReadStatusUpdateRequest.java

+2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.sprint.mission.discodeit.dto.readStatus;
22

3+
import jakarta.validation.constraints.NotNull;
34
import java.time.Instant;
45
import java.util.UUID;
56

67
public record ReadStatusUpdateRequest(
8+
@NotNull(message = "lastReadAt 필드는 반드시 값이 필요합니다.")
79
Instant lastReadAt
810
) {
911

Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
11
package com.sprint.mission.discodeit.dto.user;
22

3-
public record UserCreateRequest (
4-
String username,
5-
String email,
6-
String password
7-
){
8-
public UserCreateRequest(String username, String email, String password){
9-
validate(username, email, password);
10-
this.username = username;
11-
this.email = email;
12-
this.password = password;
13-
}
14-
private void validate(String username, String email, String password){
15-
if(username == null || username.trim().isEmpty()){
16-
throw new IllegalArgumentException("username 은 공백일 수 없습니다.");
17-
}
18-
if(email == null || !email.matches("^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$")) {
19-
throw new IllegalArgumentException("email 이 잘못되었습니다.");
20-
}
21-
if(password == null || password.trim().isEmpty()){
22-
throw new IllegalArgumentException("password 가 잘못되었습니다.");
23-
}
24-
}
3+
import jakarta.validation.constraints.Email;
4+
import jakarta.validation.constraints.NotBlank;
5+
import jakarta.validation.constraints.Size;
6+
7+
public record UserCreateRequest(
8+
@NotBlank(message = "username 은 공백일 수 없습니다.")
9+
@Size(min = 2, max = 30, message = "username 길이는 2~30자여야 합니다.")
10+
String username,
11+
12+
@NotBlank(message = "email 은 공백일 수 없습니다.")
13+
@Email(message = "email 형식이 잘못되었습니다.")
14+
String email,
15+
16+
@NotBlank(message = "password 는 공백일 수 없습니다.")
17+
@Size(min = 8, message = "password 는 최소 8자 이상이어야 합니다.")
18+
String password
19+
) {
2520

2621
}

src/main/java/com/sprint/mission/discodeit/dto/user/UserStatusUpdateRequest.java

-11
This file was deleted.

src/main/java/com/sprint/mission/discodeit/dto/user/UserUpdateRequest.java

+11
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,22 @@
22

33
import com.sprint.mission.discodeit.entity.BinaryContent;
44

5+
import jakarta.validation.constraints.Email;
6+
import jakarta.validation.constraints.NotBlank;
7+
import jakarta.validation.constraints.Size;
58
import java.util.UUID;
69

710
public record UserUpdateRequest(
11+
@NotBlank(message = "username 은 공백일 수 없습니다.")
12+
@Size(min = 2, max = 30, message = "username 길이는 2~30자여야 합니다.")
813
String newUsername,
14+
15+
@NotBlank(message = "email 은 공백일 수 없습니다.")
16+
@Email(message = "email 형식이 잘못되었습니다.")
917
String newEmail,
18+
19+
@NotBlank(message = "password 는 공백일 수 없습니다.")
20+
@Size(min = 8, message = "password 는 최소 8자 이상이어야 합니다.")
1021
String newPassword
1122
) {
1223

src/main/java/com/sprint/mission/discodeit/dto/user/UserStatusCreateRequest.java renamed to src/main/java/com/sprint/mission/discodeit/dto/userStatus/UserStatusCreateRequest.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
package com.sprint.mission.discodeit.dto.user;
1+
package com.sprint.mission.discodeit.dto.userStatus;
22

33
import com.sprint.mission.discodeit.entity.User;
44
import java.time.Instant;
5-
import java.util.UUID;
65

76
public record UserStatusCreateRequest(
87
User user,

src/main/java/com/sprint/mission/discodeit/dto/user/UserStatusUpdateByUserIdRequest.java renamed to src/main/java/com/sprint/mission/discodeit/dto/userStatus/UserStatusUpdateByUserIdRequest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.sprint.mission.discodeit.dto.user;
1+
package com.sprint.mission.discodeit.dto.userStatus;
22

33
import java.time.Instant;
44

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.sprint.mission.discodeit.dto.userStatus;
2+
3+
import java.time.Instant;
4+
import java.util.UUID;
5+
6+
public record UserStatusUpdateRequest(
7+
// isOnline은 UserStatus 내부에서만 변경한다고 생각한다.
8+
UUID UserStatusId,
9+
Instant lastConnectTime
10+
) {
11+
12+
}

0 commit comments

Comments
 (0)