Conversation
|
|
||
| import java.util.concurrent.Executor; | ||
|
|
||
| @EnableAsync |
There was a problem hiding this comment.
taskExecutor 메서드를 추후에 다른곳에서 비동기 실행하기 위해서 미리 붙여 놓으신건가요?
| @Operation(summary = "로컬 회원 로그인") | ||
| ResponseEntity<String> signIn(SignInFormRequest request, HttpServletRequest httpRequest, | ||
| HttpServletResponse response); | ||
| ResponseEntity<?> signIn(SignInFormRequest request, HttpServletRequest httpRequest, |
There was a problem hiding this comment.
저는 <?>를 사용하기보다는 명확한 타입을 정해놓고 메서드를 쪼개는 방식을 선호합니다. 아니면 customReponse< T > DTO를 생성해서 적절한 응답을 만들어주는것은 어떨까요?
| void updateAdSMSAgree(User user, UserAdSMSRequest request); | ||
| void updateAdEmailAgree(User user, UserAdEmailRequest request); | ||
| void logout(User user); | ||
| CompletableFuture<String> signInType(SignInFormRequest request, HttpSession session); |
There was a problem hiding this comment.
CompletableFuture 사용하신 이유와 무엇인지 궁금합니다.
| // 작업 유형 | ||
| private TaskType type; | ||
| // 작업에 필요한 데이터 | ||
| private Map<String, Object> payload; |
There was a problem hiding this comment.
일급 컬랙션이 아닌 Map을 사용하신 이유가 있을까요?
| Object obj = redisTemplate.opsForList().leftPop(TASK_QUEUE); | ||
| if (obj != null) { | ||
| Task task = (Task) obj; | ||
| redisTemplate.opsForSet().add(PROCESSING_SET, task); | ||
| return task; | ||
| } | ||
|
|
There was a problem hiding this comment.
- 레디스에서 작업 큐를 leftPop(가장 오래된 작업 pop?)
- 다시 그 작업을 redis에 add
- task 반환
이게 어떤 의미인가요?
| String loginId = (String) task.getPayload().get("loginId"); | ||
| String password = (String) task.getPayload().get("password"); | ||
| String sessionId = (String) task.getPayload().get("sessionId"); |
There was a problem hiding this comment.
되도록 하드 코딩보단 객체 지향적인 코드가 좋을 것 같아요
| User user = authService.attemptLogin(new SignInFormRequest(loginId, password)); | ||
|
|
||
| // Redis에 직접 세션 데이터 저장 | ||
| String sessionKey = "spring:session:sessions:" + sessionId; | ||
| redisTemplate.opsForHash().put(sessionKey, "sessionAttr:userId", user.getId()); | ||
|
|
||
| // 세션 만료 시간 설정 | ||
| redisTemplate.expire(sessionKey, 3600, TimeUnit.SECONDS); | ||
|
|
||
| log.info("사용자 {} 로그인 성공", user.getId()); | ||
| } catch (Exception e) { | ||
| log.error("로그인 실패: {}", e.getMessage(), e); | ||
| throw new RuntimeException("로그인 처리 중 오류 발생", e); | ||
| } |
There was a problem hiding this comment.
객체로 작성하면 getkey(), creatformRequest() 식으로 할 수 있어서 관리가 편할 것 같아요
|
|
||
| try { | ||
| // AuthService에 새 메서드 추가 필요 | ||
| User user = authService.attemptLogin(new SignInFormRequest(loginId, password)); |
|
|
||
| // Redis에 직접 세션 데이터 저장 | ||
| String sessionKey = "spring:session:sessions:" + sessionId; | ||
| redisTemplate.opsForHash().put(sessionKey, "sessionAttr:userId", user.getId()); |
There was a problem hiding this comment.
sessionAttr:userId 이부분이 중복되는 걸 보면 user 유일값 만 저장하시는 것같은데
기본형인 opsForValue()를 사용하지 않고
key, field, value 형식인 opsForHash()로 하신 이유가 있을까요?
| // bracket 표기법으로 내장 메트릭 값을 안전하게 읽음 | ||
| let totalRequests = data.metrics["http_reqs"] ? data.metrics["http_reqs"].count : 0; | ||
| let immediateLoginCount = data.metrics["immediate_logins"] ? data.metrics["immediate_logins"].count : 0; | ||
| let queueLoginCount = data.metrics["queue_logins"] ? data.metrics["queue_logins"].count : 0; | ||
| let failureCount = data.metrics["failures"] ? data.metrics["failures"].count : 0; | ||
| let p50 = data.metrics["http_req_duration"] ? data.metrics["http_req_duration"]["p(50)"] : 'undefined'; | ||
| let p95 = data.metrics["http_req_duration"] ? data.metrics["http_req_duration"]["p(95)"] : 'undefined'; | ||
| let p99 = data.metrics["http_req_duration"] ? data.metrics["http_req_duration"]["p(99)"] : 'undefined'; | ||
|
|
||
| let defaultSummary = textSummary(data, { indent: ' ', enableColors: true }); | ||
|
|
||
| let custom = ` | ||
| =========================== | ||
| 💻 TEST SUMMARY | ||
| =========================== | ||
| 총 요청 수: ${totalRequests} | ||
| - 즉시 로그인(200): ${immediateLoginCount} | ||
| - 대기열 진입(202): ${queueLoginCount} | ||
| - 기타 실패: ${failureCount} | ||
|
|
||
| ** 응답 시간 분포 ** | ||
| - p(50): ${p50} ms | ||
| - p(95): ${p95} ms | ||
| - p(99): ${p99} ms | ||
|
|
There was a problem hiding this comment.
굳입니다. 성능면에서 어느 정도 나왔는지 궁금합니다!
| } else { | ||
| // 대기열 사용하지 않을 때는 즉시 로그인 처리 | ||
| this.signIn(request, session); | ||
| return CompletableFuture.completedFuture("immediate-login-" + UUID.randomUUID()); |
There was a problem hiding this comment.
각 사용자를 구분하기 위해 UUID를 사용하신걸까요?
| // 작업 실패 시 재시도 횟수 카운트 | ||
| private int retryCount; | ||
| // 작업 상태 (PENDING, RETRY, FAILED, DONE) | ||
| private String status; |
There was a problem hiding this comment.
작업 상태를 enum으로 관리할 생각이 있으신가요?
| CompletableFuture<String> future = taskProducer.submitTask( | ||
| request.type(), request.payload()); | ||
|
|
||
| if(queueService.getQueueSize()> 300) { |
There was a problem hiding this comment.
여기서 300은 대기자가 300명 이상일 때 인가요..? 아니면 무슨 의미일까요? 이름으로 알기 쉽게 상수로 관리하는 건 어떻게 생각하시나요? 🤔
🚀 어떤 기능을 구현했나요 ?
🔥 어떤 문제를 마주했나요 ?
✨ 어떻게 해결했나요 ?
📝 어떤 부분에 집중해서 리뷰해야 할까요?
📚 참고 자료 및 회고