-
Notifications
You must be signed in to change notification settings - Fork 6
[COMMUNITY] community 에러 고침 및 사용자 편의성 개선 기여 #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,230 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="ko"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>PETTY 로그인 디버깅</title> | ||
| <style> | ||
| body { | ||
| font-family: Arial, sans-serif; | ||
| max-width: 800px; | ||
| margin: 0 auto; | ||
| padding: 20px; | ||
| background-color: #f5f5f5; | ||
| } | ||
| .debug-section { | ||
| background: white; | ||
| padding: 20px; | ||
| margin-bottom: 20px; | ||
| border-radius: 8px; | ||
| box-shadow: 0 2px 4px rgba(0,0,0,0.1); | ||
| } | ||
| .status { | ||
| padding: 10px; | ||
| border-radius: 4px; | ||
| margin: 10px 0; | ||
| } | ||
| .success { background-color: #d4edda; color: #155724; } | ||
| .error { background-color: #f8d7da; color: #721c24; } | ||
| .warning { background-color: #fff3cd; color: #856404; } | ||
| button { | ||
| background-color: #FF9933; | ||
| color: white; | ||
| border: none; | ||
| padding: 10px 20px; | ||
| border-radius: 4px; | ||
| cursor: pointer; | ||
| margin: 5px; | ||
| } | ||
| button:hover { background-color: #e67e00; } | ||
| .code { | ||
| background-color: #f8f9fa; | ||
| padding: 10px; | ||
| border-radius: 4px; | ||
| font-family: monospace; | ||
| white-space: pre-wrap; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <h1>🔍 PETTY 로그인 상태 디버깅</h1> | ||
|
|
||
| <div class="debug-section"> | ||
| <h2>1. JWT 토큰 상태</h2> | ||
| <div id="jwtStatus"></div> | ||
| <button onclick="checkJWT()">JWT 확인</button> | ||
| <button onclick="clearJWT()">로그아웃</button> | ||
| </div> | ||
|
|
||
| <div class="debug-section"> | ||
| <h2>2. 사용자 정보 확인</h2> | ||
| <div id="userStatus"></div> | ||
| <button onclick="checkUser()">사용자 정보 확인</button> | ||
| </div> | ||
|
|
||
| <div class="debug-section"> | ||
| <h2>3. API 엔드포인트 테스트</h2> | ||
| <div id="apiStatus"></div> | ||
| <button onclick="testAPI()">API 테스트</button> | ||
| </div> | ||
|
|
||
| <div class="debug-section"> | ||
| <h2>4. 로그인 테스트</h2> | ||
| <div> | ||
| <input type="text" id="username" placeholder="사용자명" style="margin: 5px; padding: 8px;"> | ||
| <input type="password" id="password" placeholder="비밀번호" style="margin: 5px; padding: 8px;"> | ||
| <button onclick="testLogin()">로그인 테스트</button> | ||
| </div> | ||
| <div id="loginStatus"></div> | ||
| </div> | ||
|
|
||
| <script> | ||
| function checkJWT() { | ||
| const statusDiv = document.getElementById("jwtStatus"); | ||
|
|
||
| // HttpOnly 쿠키는 JavaScript로 직접 접근할 수 없음 | ||
| statusDiv.innerHTML = ` | ||
| <div class="status warning">⚠️ HttpOnly 쿠키 방식으로 변경됨</div> | ||
| <div class="code">쿠키는 JavaScript로 직접 접근할 수 없으므로 /api/users/me로 인증 상태를 확인합니다.</div> | ||
| `; | ||
|
|
||
| // 대신 사용자 정보 API로 인증 상태 확인 | ||
| checkUser(); | ||
| } | ||
|
|
||
| function clearJWT() { | ||
| // HttpOnly 쿠키는 JavaScript로 삭제할 수 없으므로 로그아웃 API 호출 | ||
| fetch('/logout', { | ||
| method: 'POST', | ||
| credentials: 'include' | ||
| }).then(() => { | ||
| document.getElementById("jwtStatus").innerHTML = ` | ||
| <div class="status warning">🗑️ 로그아웃 요청을 보냈습니다</div> | ||
| `; | ||
| checkUser(); | ||
| }).catch(err => { | ||
| document.getElementById("jwtStatus").innerHTML = ` | ||
| <div class="status error">❌ 로그아웃 실패: ${err.message}</div> | ||
| `; | ||
| }); | ||
| } | ||
|
|
||
| async function checkUser() { | ||
| const statusDiv = document.getElementById("userStatus"); | ||
|
|
||
| try { | ||
| const res = await fetch('http://localhost:8080/api/users/me', { | ||
| credentials: 'include' // 쿠키 포함 | ||
| }); | ||
|
|
||
| if (res.ok) { | ||
| const user = await res.json(); | ||
| statusDiv.innerHTML = ` | ||
| <div class="status success">✅ 사용자 정보 조회 성공 (로그인됨)</div> | ||
| <div class="code">${JSON.stringify(user, null, 2)}</div> | ||
| `; | ||
| } else if (res.status === 401) { | ||
| statusDiv.innerHTML = ` | ||
| <div class="status error">❌ 로그인되지 않음 (401)</div> | ||
| `; | ||
| } else { | ||
| statusDiv.innerHTML = ` | ||
| <div class="status error">❌ 사용자 정보 조회 실패 (${res.status})</div> | ||
| <div class="code">응답: ${await res.text()}</div> | ||
| `; | ||
| } | ||
| } catch (err) { | ||
| statusDiv.innerHTML = ` | ||
| <div class="status error">❌ 네트워크 오류</div> | ||
| <div class="code">${err.message}</div> | ||
| `; | ||
| } | ||
| } | ||
|
|
||
| async function testAPI() { | ||
| const statusDiv = document.getElementById("apiStatus"); | ||
| const tests = [ | ||
| { name: "메인 페이지", url: "http://localhost:8080/" }, | ||
| { name: "로그인 페이지", url: "http://localhost:8080/login" }, | ||
| { name: "API 상태", url: "http://localhost:8080/api/posts?type=QNA&page=0&size=5" } | ||
| ]; | ||
|
|
||
| statusDiv.innerHTML = "<div>API 테스트 중...</div>"; | ||
|
|
||
| let results = "<h3>API 테스트 결과:</h3>"; | ||
|
|
||
| for (const test of tests) { | ||
| try { | ||
| const res = await fetch(test.url); | ||
| if (res.ok) { | ||
| results += `<div class="status success">✅ ${test.name}: 성공 (${res.status})</div>`; | ||
| } else { | ||
| results += `<div class="status error">❌ ${test.name}: 실패 (${res.status})</div>`; | ||
| } | ||
| } catch (err) { | ||
| results += `<div class="status error">❌ ${test.name}: 네트워크 오류</div>`; | ||
| } | ||
| } | ||
|
|
||
| statusDiv.innerHTML = results; | ||
| } | ||
|
|
||
| async function testLogin() { | ||
| const username = document.getElementById("username").value; | ||
| const password = document.getElementById("password").value; | ||
| const statusDiv = document.getElementById("loginStatus"); | ||
|
|
||
| if (!username || !password) { | ||
| statusDiv.innerHTML = ` | ||
| <div class="status warning">⚠️ 사용자명과 비밀번호를 입력해주세요</div> | ||
| `; | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| // Spring Security LoginFilter의 기본 경로로 요청 | ||
| const res = await fetch('http://localhost:8080/login', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json' | ||
| }, | ||
| credentials: 'include', // 쿠키 포함 | ||
| body: JSON.stringify({ username, password }) | ||
| }); | ||
|
|
||
| if (res.ok) { | ||
| const result = await res.json(); | ||
| statusDiv.innerHTML = ` | ||
| <div class="status success">✅ 로그인 성공!</div> | ||
| <div class="code">쿠키에 토큰이 저장되었습니다</div> | ||
| <div class="code">${JSON.stringify(result, null, 2)}</div> | ||
| `; | ||
|
|
||
| // 로그인 후 사용자 정보 자동 확인 | ||
| setTimeout(() => { | ||
| checkJWT(); | ||
| checkUser(); | ||
| }, 500); | ||
| } else { | ||
| const responseText = await res.text(); | ||
| statusDiv.innerHTML = ` | ||
| <div class="status error">❌ 로그인 실패 (${res.status})</div> | ||
| <div class="code">${responseText}</div> | ||
| `; | ||
| } | ||
| } catch (err) { | ||
| statusDiv.innerHTML = ` | ||
| <div class="status error">❌ 네트워크 오류</div> | ||
| <div class="code">${err.message}</div> | ||
| `; | ||
| } | ||
| } | ||
|
|
||
| // 페이지 로드 시 자동으로 상태 확인 | ||
| window.addEventListener('DOMContentLoaded', () => { | ||
| checkJWT(); | ||
| checkUser(); | ||
| }); | ||
| </script> | ||
| </body> | ||
| </html> | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -29,10 +29,22 @@ public ResponseEntity<List<CommentResponse>> getComments(@PathVariable Long post | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public ResponseEntity<?> addComment(@PathVariable Long postId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequestBody CommentRequest request, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @AuthenticationPrincipal CustomUserDetails userDetails) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String username = userDetails.getUsername(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Users user = usersRepository.findByUsername(username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long commentId = commentService.addComment(postId, request, user); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.ok().body(commentId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String username = userDetails.getUsername(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Users user = usersRepository.findByUsername(username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| System.out.println("🔥 댓글 등록 시작 - postId: " + postId + ", user: " + username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long commentId = commentService.addComment(postId, request, user); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| System.out.println("✅ 댓글 등록 완료 - commentId: " + commentId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.ok().body(commentId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| System.err.println("❌ 댓글 등록 실패: " + e.getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e.printStackTrace(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.badRequest().body("댓글 등록에 실패했습니다: " + e.getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+32
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 일관성 없는 예외 처리 개선 필요
또한 다음 개선사항들을 적용해주세요: - System.out.println("🔥 댓글 등록 시작 - postId: " + postId + ", user: " + username);
- System.out.println("✅ 댓글 등록 완료 - commentId: " + commentId);
+ log.info("댓글 등록 시작 - postId: {}, user: {}", postId, username);
+ log.info("댓글 등록 완료 - commentId: {}", commentId);- } catch (Exception e) {
- System.err.println("❌ 댓글 등록 실패: " + e.getMessage());
- e.printStackTrace();
- return ResponseEntity.badRequest().body("댓글 등록에 실패했습니다: " + e.getMessage());
+ } catch (Exception e) {
+ log.error("댓글 등록 실패 - postId: {}, user: {}", postId, username, e);
+ return ResponseEntity.badRequest().body("댓글 등록에 실패했습니다.");📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents🛠️ Refactor suggestion 예외 처리 추가는 좋지만 로깅 방식을 개선해주세요. try-catch 블록을 통한 예외 처리는 좋은 접근이지만, 다음 개선사항들을 고려해보세요:
다음과 같이 개선해보세요: +import lombok.extern.slf4j.Slf4j;
+@Slf4j
@RestController
@RequiredArgsConstructor
public class CommentController {
@PostMapping("/api/posts/{postId}/comments")
public ResponseEntity<?> addComment(@PathVariable Long postId,
@RequestBody CommentRequest request,
@AuthenticationPrincipal CustomUserDetails userDetails) {
try {
String username = userDetails.getUsername();
Users user = usersRepository.findByUsername(username);
- System.out.println("🔥 댓글 등록 시작 - postId: " + postId + ", user: " + username);
+ log.info("댓글 등록 시작 - postId: {}, user: {}", postId, username);
Long commentId = commentService.addComment(postId, request, user);
- System.out.println("✅ 댓글 등록 완료 - commentId: " + commentId);
+ log.info("댓글 등록 완료 - commentId: {}", commentId);
return ResponseEntity.ok().body(commentId);
- } catch (Exception e) {
- System.err.println("❌ 댓글 등록 실패: " + e.getMessage());
- e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ log.warn("댓글 등록 실패 - 잘못된 요청: {}", e.getMessage());
+ return ResponseEntity.badRequest().body("잘못된 요청입니다: " + e.getMessage());
+ } catch (Exception e) {
+ log.error("댓글 등록 실패", e);
return ResponseEntity.badRequest().body("댓글 등록에 실패했습니다: " + e.getMessage());
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PutMapping("/api/comments/{commentId}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -48,9 +60,21 @@ public ResponseEntity<?> updateComment(@PathVariable Long commentId, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @DeleteMapping("/api/comments/{commentId}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public ResponseEntity<?> deleteComment(@PathVariable Long commentId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @AuthenticationPrincipal CustomUserDetails userDetails) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String username = userDetails.getUsername(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Users user = usersRepository.findByUsername(username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commentService.deleteComment(commentId, user); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.noContent().build(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String username = userDetails.getUsername(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Users user = usersRepository.findByUsername(username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| System.out.println("🔥 댓글 삭제 시작 - commentId: " + commentId + ", user: " + username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commentService.deleteComment(commentId, user); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| System.out.println("✅ 댓글 삭제 완료 - commentId: " + commentId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.noContent().build(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| System.err.println("❌ 댓글 삭제 실패: " + e.getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e.printStackTrace(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.badRequest().body("댓글 삭제에 실패했습니다: " + e.getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+63
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 로깅 프레임워크 사용 권장
클래스 상단에 로거 추가: +import lombok.extern.slf4j.Slf4j;
+@Slf4j
@RestController
@RequiredArgsConstructor
public class CommentController {그리고 예외 처리 개선: - System.out.println("🔥 댓글 삭제 시작 - commentId: " + commentId + ", user: " + username);
- System.out.println("✅ 댓글 삭제 완료 - commentId: " + commentId);
+ log.info("댓글 삭제 시작 - commentId: {}, user: {}", commentId, username);
+ log.info("댓글 삭제 완료 - commentId: {}", commentId);- } catch (Exception e) {
- System.err.println("❌ 댓글 삭제 실패: " + e.getMessage());
- e.printStackTrace();
- return ResponseEntity.badRequest().body("댓글 삭제에 실패했습니다: " + e.getMessage());
+ } catch (Exception e) {
+ log.error("댓글 삭제 실패 - commentId: {}, user: {}", commentId, username, e);
+ return ResponseEntity.badRequest().body("댓글 삭제에 실패했습니다.");📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents🛠️ Refactor suggestion deleteComment 메서드의 로깅도 개선해주세요. addComment 메서드와 동일한 로깅 개선사항이 적용되어야 합니다. 앞서 제안한 것과 동일하게 SLF4J Logger를 사용하도록 개선해주세요: @DeleteMapping("/api/comments/{commentId}")
public ResponseEntity<?> deleteComment(@PathVariable Long commentId,
@AuthenticationPrincipal CustomUserDetails userDetails) {
try {
String username = userDetails.getUsername();
Users user = usersRepository.findByUsername(username);
- System.out.println("🔥 댓글 삭제 시작 - commentId: " + commentId + ", user: " + username);
+ log.info("댓글 삭제 시작 - commentId: {}, user: {}", commentId, username);
commentService.deleteComment(commentId, user);
- System.out.println("✅ 댓글 삭제 완료 - commentId: " + commentId);
+ log.info("댓글 삭제 완료 - commentId: {}", commentId);
return ResponseEntity.noContent().build();
- } catch (Exception e) {
- System.err.println("❌ 댓글 삭제 실패: " + e.getMessage());
- e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ log.warn("댓글 삭제 실패 - 잘못된 요청: {}", e.getMessage());
+ return ResponseEntity.badRequest().body("잘못된 요청입니다: " + e.getMessage());
+ } catch (Exception e) {
+ log.error("댓글 삭제 실패", e);
return ResponseEntity.badRequest().body("댓글 삭제에 실패했습니다: " + e.getMessage());
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.github.petty.users.dto.CustomUserDetails; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.github.petty.users.entity.Users; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.github.petty.users.repository.UsersRepository; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.validation.Valid; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 컴파일 오류: 잘못된 validation 패키지 import 파이프라인 오류에 따르면 Spring Boot 2.x를 사용 중이라면: -import jakarta.validation.Valid;
+import javax.validation.Valid;Spring Boot 3.x를 사용 중이라면 의존성을 확인하세요: <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>🧰 Tools🪛 GitHub Actions: Java CI with Gradle[error] 10-10: Compilation failed: package 'jakarta.validation' does not exist. Missing import for @Valid annotation. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.data.domain.Page; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.data.domain.Pageable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -26,17 +27,27 @@ public class PostController { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final UsersRepository usersRepository; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PostMapping | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public ResponseEntity<?> create(@RequestBody PostRequest request, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public ResponseEntity<?> create(@RequestBody @Valid PostRequest request, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @AuthenticationPrincipal CustomUserDetails userDetails) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String username = userDetails.getUsername(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Users user = usersRepository.findByUsername(username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long id = postService.save(request, user); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.ok(Map.of("id", id)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String username = userDetails.getUsername(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Users user = usersRepository.findByUsername(username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (user == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.status(401).body("사용자를 찾을 수 없습니다."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long id = postService.save(request, user); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.ok(Map.of("id", id)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (IllegalArgumentException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.badRequest().body(e.getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.status(500).body("게시글 작성에 실패했습니다."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PutMapping("/{id}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public ResponseEntity<?> update(@PathVariable Long id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequestBody PostRequest request, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequestBody @Valid PostRequest request, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @AuthenticationPrincipal CustomUserDetails userDetails) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String username = userDetails.getUsername(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Users user = usersRepository.findByUsername(username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -50,8 +61,8 @@ public ResponseEntity<?> delete(@PathVariable Long id, | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @AuthenticationPrincipal CustomUserDetails userDetails) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String username = userDetails.getUsername(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Users user = usersRepository.findByUsername(username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| postService.delete(id, user); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.ok().build(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String postType = postService.delete(id, user); // 🔥 삭제된 게시글의 타입 반환 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.ok(Map.of("postType", postType)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @GetMapping | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -70,9 +81,26 @@ public ResponseEntity<PostDetailResponse> getPost(@PathVariable Long id) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PostMapping("/{id}/like") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public ResponseEntity<?> likePost(@PathVariable Long id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @AuthenticationPrincipal CustomUserDetails userDetails) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String username = userDetails.getUsername(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Users user = usersRepository.findByUsername(username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int newCount = postService.toggleLike(id, user); // 좋아요 또는 취소 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.ok(Map.of("likeCount", newCount)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String username = userDetails.getUsername(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Users user = usersRepository.findByUsername(username); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (user == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.status(401).body("사용자를 찾을 수 없습니다."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int newCount = postService.toggleLike(id, user); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.ok(Map.of("likeCount", newCount)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (IllegalArgumentException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.badRequest().body(e.getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.status(500).body("좋아요 처리에 실패했습니다."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 🔥 기존 데이터 업데이트를 위한 임시 엔드포인트 (관리자용) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PostMapping("/update-counts") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public ResponseEntity<?> updateAllPostCounts() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| postService.updateAllPostCounts(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ResponseEntity.ok(Map.of("message", "모든 게시글의 댓글 수와 좋아요 수가 업데이트되었습니다.")); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+100
to
105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 보안 취약점: 관리자 권한 체크가 없습니다! 이 엔드포인트는 모든 게시글의 카운트를 업데이트하는 중요한 작업을 수행하지만, 권한 체크가 없어 누구나 호출할 수 있습니다. 관리자 권한 체크를 추가하세요: @PostMapping("/update-counts")
+@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<?> updateAllPostCounts() {
postService.updateAllPostCounts();
return ResponseEntity.ok(Map.of("message", "모든 게시글의 댓글 수와 좋아요 수가 업데이트되었습니다."));
}또는 요청자의 권한을 확인하는 로직을 추가하세요: @PostMapping("/update-counts")
-public ResponseEntity<?> updateAllPostCounts() {
+public ResponseEntity<?> updateAllPostCounts(@AuthenticationPrincipal CustomUserDetails userDetails) {
+ // 관리자 권한 확인
+ if (!userDetails.getAuthorities().stream()
+ .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
+ return ResponseEntity.status(403).body("관리자 권한이 필요합니다.");
+ }
postService.updateAllPostCounts();
return ResponseEntity.ok(Map.of("message", "모든 게시글의 댓글 수와 좋아요 수가 업데이트되었습니다."));
}📝 Committable suggestion
Suggested change
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,15 @@ | ||
| package io.github.petty.community.dto; | ||
|
|
||
| import jakarta.validation.constraints.NotBlank; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 검증 어노테이션 패키지 불일치 문제
다음과 같이 수정해주세요: -import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
-import javax.validation.constraints.Size;Also applies to: 7-7 🧰 Tools🪛 GitHub Actions: Java CI with Gradle[error] 3-3: Compilation failed: package 'jakarta.validation.constraints' does not exist. Missing import for NotBlank annotation. 🤖 Prompt for AI Agents |
||
| import lombok.Getter; | ||
| import lombok.Setter; | ||
|
|
||
| import javax.validation.constraints.Size; | ||
|
|
||
| @Getter | ||
| @Setter | ||
| public class CommentRequest { | ||
| @NotBlank | ||
| @Size(max=500) | ||
| private String content; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
프로덕션 배포 시 보안 위험이 있습니다.
이 디버깅 페이지는 개발용으로는 유용하지만 다음과 같은 보안 우려사항이 있습니다:
다음과 같은 보안 조치를 권장합니다:
또한 프로덕션 빌드 시 이 파일을 제외하는 설정을 추가하거나,
/debug/경로로 이동시켜 프로덕션에서 접근을 차단하는 것을 고려해주세요.📝 Committable suggestion
🤖 Prompt for AI Agents