Skip to content

Commit c8c41a1

Browse files
committed
⚡ MarkSphere v1.0.10
입력 데이터 검증 로직 개선(dto에서 1차적으로 검증. 이후, DB에서 검증)
1 parent aa45a97 commit c8c41a1

19 files changed

Lines changed: 112 additions & 24 deletions

File tree

src/main/java/com/sonkim/bookmarking/common/exception/GlobalExceptionHandler.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66
import org.springframework.http.HttpStatus;
77
import org.springframework.http.ResponseEntity;
88
import org.springframework.security.authorization.AuthorizationDeniedException;
9+
import org.springframework.validation.BindingResult;
910
import org.springframework.web.HttpRequestMethodNotSupportedException;
11+
import org.springframework.web.bind.MethodArgumentNotValidException;
1012
import org.springframework.web.bind.annotation.ExceptionHandler;
1113
import org.springframework.web.bind.annotation.RestControllerAdvice;
1214
import org.springframework.web.servlet.resource.NoResourceFoundException;
1315

1416
import java.time.LocalDateTime;
1517
import java.util.HashMap;
1618
import java.util.Map;
19+
import java.util.stream.Collectors;
1720

1821
@Slf4j
1922
@Hidden
@@ -63,6 +66,21 @@ public ResponseEntity<?> handleNoResourceFoundException(NoResourceFoundException
6366
return buildResponse(HttpStatus.NOT_FOUND, e.getMessage());
6467
}
6568

69+
// MethodArgumentNotValidException 처리
70+
@ExceptionHandler(MethodArgumentNotValidException.class)
71+
public ResponseEntity<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
72+
BindingResult bindingResult = e.getBindingResult();
73+
74+
// 에러 메시지 가공: "필드명: 에러내용, 필드명: 에러내용"
75+
String errorMessage = bindingResult.getFieldErrors().stream()
76+
.map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
77+
.collect(Collectors.joining(", "));
78+
79+
log.warn("400(BAD_REQUEST) 유효성 검사 실패: {}", errorMessage);
80+
81+
return buildResponse(HttpStatus.BAD_REQUEST, errorMessage);
82+
}
83+
6684
// '좋아요' 중복 에러 처리
6785
@ExceptionHandler(DuplicateBookmarkLikeException.class)
6886
public ResponseEntity<?> handleDuplicateBookmarkLikeException(DuplicateBookmarkLikeException e) {

src/main/java/com/sonkim/bookmarking/domain/bookmark/controller/BookmarkController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import io.swagger.v3.oas.annotations.responses.ApiResponses;
1313
import io.swagger.v3.oas.annotations.tags.Tag;
1414
import io.swagger.v3.oas.annotations.responses.ApiResponse;
15+
import jakarta.validation.Valid;
1516
import lombok.RequiredArgsConstructor;
1617
import lombok.extern.slf4j.Slf4j;
1718
import org.springframework.http.ResponseEntity;
@@ -52,7 +53,7 @@ public ResponseEntity<BookmarkResponseDto> getBookmark(
5253
@PatchMapping("/{bookmarkId}")
5354
public ResponseEntity<String> updateBookmark(@AuthenticationPrincipal UserDetailsImpl userDetails,
5455
@PathVariable("bookmarkId") Long bookmarkId,
55-
@RequestBody BookmarkRequestDto bookmarkRequestDto) {
56+
@RequestBody @Valid BookmarkRequestDto bookmarkRequestDto) {
5657
log.info("userId: {}, bookmarkId: {} 북마크 수정 요청", userDetails.getId(), bookmarkId);
5758
bookmarkService.updateBookmark(userDetails.getId(), bookmarkId, bookmarkRequestDto);
5859
return ResponseEntity.ok("bookmarkId: " + bookmarkId + " updated");

src/main/java/com/sonkim/bookmarking/domain/bookmark/controller/TeamBookmarkController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import io.swagger.v3.oas.annotations.responses.ApiResponse;
1313
import io.swagger.v3.oas.annotations.responses.ApiResponses;
1414
import io.swagger.v3.oas.annotations.tags.Tag;
15+
import jakarta.validation.Valid;
1516
import lombok.RequiredArgsConstructor;
1617
import lombok.extern.slf4j.Slf4j;
1718
import org.springframework.http.HttpStatus;
@@ -39,7 +40,7 @@ public class TeamBookmarkController {
3940
@Idempotent
4041
public ResponseEntity<BookmarkResponseDto> createBookmark(@AuthenticationPrincipal UserDetailsImpl userDetails,
4142
@PathVariable Long groupId,
42-
@RequestBody BookmarkRequestDto bookmarkRequestDto) {
43+
@RequestBody @Valid BookmarkRequestDto bookmarkRequestDto) {
4344
log.info("userId: {}, url: {} 북마크 생성 요청", userDetails.getId(), bookmarkRequestDto.getUrl());
4445
BookmarkResponseDto responseDto = bookmarkService.createBookmark(userDetails.getId(), groupId, bookmarkRequestDto);
4546

src/main/java/com/sonkim/bookmarking/domain/bookmark/dto/BookmarkRequestDto.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,35 @@
11
package com.sonkim.bookmarking.domain.bookmark.dto;
22

3+
import jakarta.validation.constraints.Max;
4+
import jakarta.validation.constraints.Min;
5+
import jakarta.validation.constraints.NotBlank;
6+
import jakarta.validation.constraints.Size;
37
import lombok.Data;
48

59
import java.util.List;
610

711
@Data
812
public class BookmarkRequestDto {
913
private Long categoryId;
14+
15+
@NotBlank(message = "URL은 필수입니다.")
1016
private String url;
17+
18+
@Size(max = 100, message = "제목은 100자를 초과할 수 없습니다.")
1119
private String title;
20+
21+
@Size(max = 5000)
1222
private String description;
23+
24+
@Size(max = 10, message = "태그는 최대 10개까지만 등록할 수 있습니다.")
1325
private List<String> tagNames;
26+
27+
@Min(value = -90, message = "위도는 -90 이상이어야 합니다.")
28+
@Max(value = 90, message = "위도는 90 이하여야 합니다.")
1429
private Double latitude;
30+
31+
@Min(value = -180, message = "경도는 -180 이상이어야 합니다.")
32+
@Max(value = 180, message = "경도는 180 이하여야 합니다.")
1533
private Double longitude;
1634
private String imageKey; // S3에 직접 업로드한 경우의 파일 키
1735
private String originalImageUrl; // OG 정보의 이미지 URL

src/main/java/com/sonkim/bookmarking/domain/category/controller/CategoryController.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.swagger.v3.oas.annotations.responses.ApiResponse;
88
import io.swagger.v3.oas.annotations.responses.ApiResponses;
99
import io.swagger.v3.oas.annotations.tags.Tag;
10+
import jakarta.validation.Valid;
1011
import lombok.RequiredArgsConstructor;
1112
import lombok.extern.slf4j.Slf4j;
1213
import org.springframework.http.HttpStatus;
@@ -43,7 +44,7 @@ public ResponseEntity<List<CategoryDto.CategoryResponseDto>> getCategories(@Path
4344
@PostMapping("/groups/{groupId}/categories")
4445
public ResponseEntity<List<CategoryDto.CategoryResponseDto>> createCategory(@AuthenticationPrincipal UserDetailsImpl userDetails,
4546
@PathVariable("groupId") Long groupId,
46-
@RequestBody CategoryDto.CategoryRequestDto request) {
47+
@RequestBody @Valid CategoryDto.CategoryRequestDto request) {
4748
List<CategoryDto.CategoryResponseDto> updatedCategories = categoryService.createCategory(userDetails.getId(), groupId, request);
4849
return ResponseEntity.status(HttpStatus.CREATED).body(updatedCategories);
4950
}
@@ -57,7 +58,7 @@ public ResponseEntity<List<CategoryDto.CategoryResponseDto>> createCategory(@Aut
5758
@PatchMapping("/categories/{categoryId}")
5859
public ResponseEntity<List<CategoryDto.CategoryResponseDto>> updateCategory(@AuthenticationPrincipal UserDetailsImpl userDetails,
5960
@PathVariable("categoryId") Long categoryId,
60-
@RequestBody CategoryDto.CategoryRequestDto request) {
61+
@RequestBody @Valid CategoryDto.CategoryRequestDto request) {
6162
List<CategoryDto.CategoryResponseDto> updatedCategories = categoryService.updateCategory(userDetails.getId(), categoryId, request);
6263
return ResponseEntity.ok(updatedCategories);
6364
}
@@ -81,9 +82,9 @@ public ResponseEntity<List<CategoryDto.CategoryResponseDto>> deleteCategory(@Aut
8182
public ResponseEntity<Void> updateCategoryPositions(
8283
@AuthenticationPrincipal UserDetailsImpl userDetails,
8384
@PathVariable Long groupId,
84-
@RequestBody List<CategoryDto.UpdatePositionRequestDto> requests
85+
@RequestBody @Valid CategoryDto.CategoryOrderUpdateRequest requests
8586
) {
86-
categoryService.updateCategoryPositions(userDetails.getId(), groupId, requests);
87+
categoryService.updateCategoryPositions(userDetails.getId(), groupId, requests.getCategories());
8788
return ResponseEntity.ok().build();
8889
}
8990
}

src/main/java/com/sonkim/bookmarking/domain/category/dto/CategoryDto.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
package com.sonkim.bookmarking.domain.category.dto;
22

3+
import jakarta.validation.Valid;
4+
import jakarta.validation.constraints.Min;
5+
import jakarta.validation.constraints.NotBlank;
6+
import jakarta.validation.constraints.NotNull;
7+
import jakarta.validation.constraints.Size;
38
import lombok.AllArgsConstructor;
49
import lombok.Builder;
510
import lombok.Data;
611
import lombok.NoArgsConstructor;
712

13+
import java.util.List;
14+
815
public class CategoryDto {
916

1017
// 카테고리 생성,갱신 요청 DTO
1118
@Data
1219
public static class CategoryRequestDto {
20+
@NotBlank(message = "카테고리 이름은 필수입니다.")
21+
@Size(max = 20, message = "카테고리 이름은 20를 초과할 수 없습니다.")
1322
private String name;
1423
}
1524

@@ -27,7 +36,19 @@ public static class CategoryResponseDto {
2736
// 카테고리 순서 업데이트 요청 DTO
2837
@Data
2938
public static class UpdatePositionRequestDto {
39+
@NotNull(message = "카테고리 ID는 필수입니다.")
3040
private Long categoryId;
41+
42+
@NotNull(message = "순서 값은 필수입니다.")
43+
@Min(value = 0, message = "순서는 0 이상이어야 합니다.")
3144
private Integer position;
3245
}
46+
47+
@Data
48+
public static class CategoryOrderUpdateRequest {
49+
@Valid
50+
@NotNull(message = "변경할 카테고리 목록은 필수입니다.")
51+
@Size(min = 1, message = "최소 1개 이상의 카테고리가 있어야 합니다.")
52+
private List<UpdatePositionRequestDto> categories;
53+
}
3354
}

src/main/java/com/sonkim/bookmarking/domain/comment/controller/CommentController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.swagger.v3.oas.annotations.responses.ApiResponse;
1111
import io.swagger.v3.oas.annotations.responses.ApiResponses;
1212
import io.swagger.v3.oas.annotations.tags.Tag;
13+
import jakarta.validation.Valid;
1314
import lombok.RequiredArgsConstructor;
1415
import lombok.extern.slf4j.Slf4j;
1516
import org.springframework.http.ResponseEntity;
@@ -32,7 +33,7 @@ public class CommentController {
3233
public ResponseEntity<CommentDto.CreateResponseDto> createComment(
3334
@AuthenticationPrincipal UserDetailsImpl userDetails,
3435
@Parameter(description = "댓글/답글을 작성할 북마크 ID") @PathVariable("bookmarkId") Long bookmarkId,
35-
@RequestBody CommentDto.CreateRequestDto request) {
36+
@RequestBody @Valid CommentDto.CreateRequestDto request) {
3637
CommentDto.CreateResponseDto newCommentDto = commentService.createComment(userDetails.getId(), bookmarkId, request);
3738
return ResponseEntity.status(201).body(newCommentDto);
3839
}

src/main/java/com/sonkim/bookmarking/domain/comment/dto/CommentDto.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.sonkim.bookmarking.domain.comment.dto;
22

3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Size;
35
import lombok.Builder;
46
import lombok.Data;
57

@@ -10,7 +12,10 @@ public class CommentDto {
1012
// 댓글, 답글 등록 DTO
1113
@Data
1214
public static class CreateRequestDto {
15+
@NotBlank(message = "댓글 내용은 필수입니다.")
16+
@Size(max = 100, message = "댓글은 100자를 초과할 수 없습니다.")
1317
private String content;
18+
1419
private Long parentId; // 최상위 댓글이면 null, 답글이면 부모 댓글 ID
1520
}
1621

src/main/java/com/sonkim/bookmarking/domain/mypage/controller/MyPageController.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import io.swagger.v3.oas.annotations.responses.ApiResponse;
1616
import io.swagger.v3.oas.annotations.responses.ApiResponses;
1717
import io.swagger.v3.oas.annotations.tags.Tag;
18+
import jakarta.validation.Valid;
1819
import lombok.RequiredArgsConstructor;
1920
import org.springframework.http.ResponseEntity;
2021
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@@ -56,7 +57,7 @@ public ResponseEntity<PresignedUrlDto> getProfileImageUploadUrl(
5657
@ApiResponse(responseCode = "200", description = "프로필 수정 성공")
5758
@PatchMapping("/profile")
5859
public ResponseEntity<Void> updateMyProfile(@AuthenticationPrincipal UserDetailsImpl userDetails,
59-
@RequestBody MyProfileDto.UpdateRequestDto updateDto) {
60+
@RequestBody @Valid MyProfileDto.UpdateRequestDto updateDto) {
6061
myPageService.updateProfile(userDetails.getId(), updateDto);
6162
return ResponseEntity.ok().build();
6263
}
@@ -68,7 +69,7 @@ public ResponseEntity<Void> updateMyProfile(@AuthenticationPrincipal UserDetails
6869
})
6970
@PatchMapping("/password")
7071
public ResponseEntity<Void> changePassword(@AuthenticationPrincipal UserDetailsImpl userDetails,
71-
@RequestBody PasswordDto passwordDto) {
72+
@RequestBody @Valid PasswordDto passwordDto) {
7273
myPageService.changePassword(userDetails.getId(), passwordDto);
7374
return ResponseEntity.ok().build();
7475
}

src/main/java/com/sonkim/bookmarking/domain/mypage/dto/MyProfileDto.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.sonkim.bookmarking.domain.mypage.dto;
22

3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Size;
35
import lombok.Builder;
46
import lombok.Data;
57

@@ -14,7 +16,10 @@ public static class MyProfileResponseDto {
1416

1517
@Data
1618
public static class UpdateRequestDto {
19+
@NotBlank(message = "닉네임은 필수입니다.")
20+
@Size(max = 20, message = "닉네임은 20자를 초과할 수 없습니다.")
1721
private String nickname;
22+
1823
private String imageKey;
1924
}
2025
}

0 commit comments

Comments
 (0)