1. [문제 인식 및 정의] 2. [해결 방안] 2-1. [의사결정 과정] 2-2. [해결 과정] 3. [해결 완료] 3-1. [회고] 3-2. [전후 데이터 비교]
1️⃣ 비밀번호 Valid 수정
1. [문제 인식 및 정의]
레벨 1 에서 Validation 을 수정했습니다. 비밀번호에 대한 검증인데, 회원가입 시에는 적용되고 있지 않았습니다. 새로운 비밀번호 수정 시에만 Valid 를 적용해주고 있었습니다.
회원가입 시에는 비밀번호에 대한 유효성 검증이 적용되지 않았고, 새로운 비밀번호 수정 시에만 유효성 검증이 적용되는 문제점이 존재합니다.
이로 인해, 회원가입 시에는 비밀번호 제약이 적용되지 않아 보안상 문제가 발생할 수 있다. 따라서, 비밀번호 검증 로직을 회원가입에도 적용해야 합니다.
2. [해결 방안]
1) Entity DB 제약 추가
보통 자세한 제약은 DTO 딴에서 막는 것이 낫습니다. 비밀번호 같은 민감한 로직은 DB에서 처리하는 것이 아닌, 애플리케이션에서 처리하는 게 보안적으로도 맞기 때문입니다. RDBMS에서 문자열에 대해 정규표현식 같은 정교한 제약은 제한적이거나 비효율적인 것도 이유입니다. 물론 CHECK 제약조건 같은 건 있지만, MySQL이나 MariaDB에서는 한계가 있고, 복잡한 표현식은 지원이 미약합니다.
그리하여 최소한의 제약인 nullable 만 추가했습니다.
User.java
@Column(nullable = false)
private String password;
2) SignupRequest 제약 추가
@Size(min = 8, message = "비밀번호는 최소 8자 이상이어야 합니다.")
@Pattern(regexp = "^(?=.*\\d)(?=.*[A-Z]).*$", message = "숫자와 대문자를 포함해야 합니다.")
@NotBlank
private String password;
- SignupRequest 에 적용 했습니다.
3. [해결 완료 - 회고]
회원 가입 시에도 비밀번호에 대한 동일한 제약이 적용됩니다.
이를 통해 비밀번호 강도를 강화하고, 보안상의 취약점을 예방할 수 있습니다. 즉, 가입 시부터 강력한 비밀번호 정책을 적용함으로써 불필요한 보안 리스크를 줄일 수 있습니다.
회원가입 시 비밀번호 제약을 적용하여 보안이 강화되었습니다. 이제 강력한 비밀번호 정책을 통해 보안 취약점을 예방할 수 있습니다.
로그인 과정에서는 비밀번호 검증을 @Valid 어노테이션을 사용하지 않았는데, 로그인은 이미 등록된 정보를 사용하는 과정이므로 비밀번호 제약이 필요하지 않다고 판단했습니다.
2️⃣ Interceptor 제약 추가
1. [문제 인식 및 정의]
role이 비어있는 상황도 있을 수 있다. 라는 조언을 듣게 되었습니다. 코드 흐름에서는 userRole이 빈 값으로 설정될 가능성은 거의 없으나, JWT 토큰 자체에 문제가 있는 경우가 있을 수도 있기 때문에 제약을 한번 추가해보고자 했습니다.
2. [해결 방안]
Interceptor 에서 가져온 요청에 userRole 이 비어있을 경우를 위해 제약사항을 추가할 것입니다.
이때 401 에러를 반환해줄겁니다.
// role 이 비어있는 경우도 있을 경우 401 에러 반환합니다. Unauthorized
if (role == null || role.isEmpty()) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "User role is missing.");
return false;
}
이런 코드를 추가함으로써 userRole 이 null 일 경우도 체크해주고자 했습니다.
3. [해결 완료 - 회고]
request.getAttribute("userRole")로 값을 가져올 때, 해당 속성이 없거나 null인 경우 role 변수에는 null이 할당됩니다. 이 자체로는 에러가 발생하지 않지만 이후 로직에서 role이 null일 경우 NullPointerException 등 문제가 발생할 수 있으므로, 조건 처리를 통해 이를 사전에 방지할 수 있습니다.
이와 같은 검증 로직을 추가하는 것은 보안적인 측면에서 매우 중요하다는 것을 깨달았습니다. 사용자의 role 정보가 없거나 잘못된 경우, 그에 맞는 처리 없이 로직이 진행되면 예기치 않은 오류가 발생할 수 있습니다. 이를 방지하기 위해 사전 검증을 통해 안전하게 처리하는 것이 보안상 더욱 신뢰할 수 있는 시스템을 만드는 데 기여할 겁니다. 또한, 이런 방식으로 예외를 미리 처리하는 것은 시스템 안정성뿐만 아니라 사용자 경험을 높이는 데도 도움이 됩니다.
+) 실제로 테스트 코드를 작성하다보니 알게 된 것인데, 이런식으로 토큰이 잘못 발급되면 ! null 일때가 존재했습니다.
3️⃣ Comment Admin 수정 ① CommentAdminController ResponseEntity 수정
1. [문제 인식 및 정의]
- Comment Controller 를 확인해보니, 200 OK 를 반환하고 있었습니다.
- 200 OK 의 경우, 삭제 요청 처리 후, 응답 본문에 메시지나 데이터를 담을 경우 사용한다고 알고 있습니다.
- 해당 OK 를 NO CONTENT 인 204 상태코드를 줄 것입니다.
2. [해결 방안]
public ResponseEntity<Void> deleteComment(@PathVariable long commentId) {
commentAdminService.deleteComment(commentId);
return ResponseEntity.noContent().build();
}
- void 인 부분을 지우고, ReponseEntity 가 반환되도록 했습니다.
- 이렇게 되면 200 OK 가 아닌, 204 NO CONTENT 가 반환됩니다.
3. [해결 완료 - 회고]
- RESTful한 의도 표현이 되었다고 생각합니다. DELETE는 리소스를 삭제하는 동작이기 때문에, 결과적으로 아무것도 남지 않는 게 자연스럽습니다. 그래서 "응답 본문도 없음"을 의미하는 204가 잘 어울린다고 판단했습니다.
- 삭제 후에 굳이 "삭제되었습니다" 같은 본문 메시지를 주지 않아도, 클라이언트는 상태 코드만 보고 성공 여부를 판단할 수 있습니다.
- 204를 사용할 경우, 정말로 본문을 포함하지 않아야됩니다. 그렇지 않으면 클라이언트가 파싱에 실패할 수 있습니다.
- 만약 클라이언트에게 추가 정보를 주고 싶다면, 200 OK에 JSON 메시지를 포함하는 식으로 해야합니다.
4️⃣ Comment Admin 수정 ② CommentAdminService 리팩토링
1. [문제 인식 및 정의]
- CommentAdmin Service 에서 삭제만을 처리하고 있었습니다.
public void deleteComment(long commentId) {
commentRepository.deleteById(commentId);
}
- commentId 의 존재여부를 검증하지 않아, 해당 부분을 수정하고자 했습니다.
- ID가 존재하지 않을 때 아무런 예외 처리를 하지 않으면 발생할 수 있는 문제가 있을 수 있습니다. 바로, 서버 에러인 500 상태코드가 반환될 수 있다는 것입니다.
2. [해결 방안]
@Transactional
public void deleteComment(long commentId) {
if (!commentRepository.existsById(commentId)) {
throw new InvalidRequestException("Comment not found");
}
commentRepository.deleteById(commentId);
}
}
- 이런식으로 아이디가 존재하지 않을 경우 에러를 반환할 수 있도록 수정하였습니다.
3. [해결 완료 - 회고]
- 이렇게 수정하게 된다면, 정확한 응답 제어 존재하지 않는 리소스에 대해 404 Not Found 응답을 줄 수 있습니다.
- 비즈니스 로직 일관성 유지 삭제하려는 리소스가 실제 존재하는지 명확히 파악 가능합니다.
- RESTful 설계는 "의도"를 응답 코드에 담는 것이라고 합니다. 404 Not Found 는 단순히 "없는 걸 삭제하려 했다"는 의도를 명확히 표현하기 때문에, 적절한 수정이라고 생각했습니다.
'백엔드 부트캠프 > 문제풀이' 카테고리의 다른 글
[일정 관리 앱 만들기] 트러블 슈팅 (0) | 2025.03.26 |
---|---|
Lv 3. 도전 계산기 만들기 (1) | 2025.03.06 |
Lv 2. 클래스를 적용해 기본적인 연산을 수행할 수 있는 계산기 만들기 (0) | 2025.02.27 |
Lv 1. 클래스 없이 기본적인 연산을 수행할 수 있는 계산기 만들기 (0) | 2025.02.26 |
[Chapter02-1] 클래스와 객체 실습과제 (0) | 2025.02.25 |