Conversation
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (6)
📝 WalkthroughWalkthrough사용자의 마지막 로그인 및 활동 시간을 추적하는 기능을 추가합니다. Spring AOP를 통해 컨트롤러 메서드 실행 후 활동 타임스탠프를 자동으로 업데이트하고, 로그인 시 로그인 타임스탠프를 기록하며, User 엔티티와 데이터베이스에 관련 필드를 추가합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller
participant Aspect
participant UserActivityService
participant UserRepository
participant Database
Client->>Controller: 요청 (로그인 또는 일반 요청)
activate Controller
Controller->>UserActivityService: updateLastLoginAt(userId) [로그인 시에만]
activate UserActivityService
UserActivityService->>UserRepository: findById(userId)
activate UserRepository
UserRepository->>Database: SELECT user
Database-->>UserRepository: User 객체
UserRepository-->>UserActivityService: User 반환
deactivate UserRepository
UserActivityService->>UserActivityService: user.updateLastLoginAt(now())
UserActivityService->>Database: UPDATE user.last_login_at
deactivate UserActivityService
Controller-->>Client: 응답
deactivate Controller
Note over Aspect: 컨트롤러 메서드 실행 후 AOP 트리거
Aspect->>Aspect: 요청 컨텍스트에서 userId 추출
Aspect->>UserActivityService: updateLastActivityAt(userId)
activate UserActivityService
UserActivityService->>UserRepository: findById(userId)
activate UserRepository
UserRepository->>Database: SELECT user
Database-->>UserRepository: User 객체
UserRepository-->>UserActivityService: User 반환
deactivate UserRepository
UserActivityService->>UserActivityService: user.updateLastActivityAt(now())
UserActivityService->>Database: UPDATE user.last_activity_at
deactivate UserActivityService
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Comment |
There was a problem hiding this comment.
Pull request overview
사용자의 마지막 로그인 시각(last_login_at) 및 마지막 활동 시각(last_activity_at) 을 DB에 저장하고, /users/refresh 및 인증된 API 호출 이후 이를 자동 갱신하도록 연동하는 변경입니다.
Changes:
users테이블에last_login_at,last_activity_at컬럼 추가 (Flyway V38)/users/refresh에서last_login_at갱신 로직 연결- 컨트롤러 호출 이후
last_activity_at를 갱신하는 AOP(Aspect) 및 관련 서비스 추가
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/resources/db/migration/V38__add_last_login_and_last_activity_to_users.sql | users 테이블에 마지막 로그인/활동 컬럼 추가 |
| src/main/java/gg/agit/konect/global/auth/aop/UserActivityUpdateAspect.java | 컨트롤러 실행 후 인증 사용자에 대해 활동 시각 갱신 |
| src/main/java/gg/agit/konect/domain/user/service/UserActivityService.java | last_login_at / last_activity_at 갱신 서비스 로직 추가 |
| src/main/java/gg/agit/konect/domain/user/model/User.java | User 엔티티에 lastLoginAt/lastActivityAt 필드 및 업데이트 메서드 추가 |
| src/main/java/gg/agit/konect/domain/user/controller/UserController.java | refresh 시 last_login_at 갱신 호출 추가 |
| build.gradle | AOP 스타터 의존성 추가 |
| @Column(name = "last_login_at", columnDefinition = "TIMESTAMP") | ||
| private LocalDateTime lastLoginAt; | ||
|
|
||
| @Column(name = "last_activity_at", columnDefinition = "TIMESTAMP") |
There was a problem hiding this comment.
Flyway 마이그레이션에서는 last_login_at/last_activity_at 컬럼 타입이 DATETIME인데, 엔티티는 columnDefinition="TIMESTAMP"로 선언되어 있어 spring.jpa.hibernate.ddl-auto=validate 환경에서 스키마 검증 실패가 발생할 수 있습니다. DB 타입과 엔티티 매핑을 동일하게 맞춰 주세요(마이그레이션을 TIMESTAMP로 바꾸거나, 엔티티의 columnDefinition을 DATETIME/기본 매핑으로 변경).
| @Column(name = "last_login_at", columnDefinition = "TIMESTAMP") | |
| private LocalDateTime lastLoginAt; | |
| @Column(name = "last_activity_at", columnDefinition = "TIMESTAMP") | |
| @Column(name = "last_login_at") | |
| private LocalDateTime lastLoginAt; | |
| @Column(name = "last_activity_at") |
| @After("execution(* gg.agit.konect..controller..*(..))") | ||
| public void updateLastActivity() { |
There was a problem hiding this comment.
현재 @After 어드바이스는 컨트롤러 메서드가 예외를 던져도 실행되므로, 5xx/4xx 등 실패 응답에서도 last_activity_at이 갱신됩니다. "인증된 API 요청 이후"를 성공 요청으로 한정하려면 @AfterReturning으로 변경하는 쪽이 의도에 더 맞습니다.
| ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); | ||
| if (attributes == null) { | ||
| return; | ||
| } | ||
|
|
||
| HttpServletRequest request = attributes.getRequest(); |
There was a problem hiding this comment.
RequestContextHolder.getRequestAttributes() 결과를 바로 ServletRequestAttributes로 캐스팅하고 있어(현재는 null만 체크) 비-서블릿 컨텍스트에서 호출되면 ClassCastException이 날 수 있습니다. instanceof ServletRequestAttributes 확인 후 캐스팅하도록 방어 코드를 추가해 주세요.
| ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); | |
| if (attributes == null) { | |
| return; | |
| } | |
| HttpServletRequest request = attributes.getRequest(); | |
| Object attributes = RequestContextHolder.getRequestAttributes(); | |
| if (attributes == null || !(attributes instanceof ServletRequestAttributes)) { | |
| return; | |
| } | |
| HttpServletRequest request = ((ServletRequestAttributes) attributes).getRequest(); |
| public void updateLastLoginAt(Integer userId) { | ||
| if (userId == null) { | ||
| return; | ||
| } | ||
|
|
||
| userRepository.getById(userId).updateLastLoginAt(LocalDateTime.now()); | ||
| } |
There was a problem hiding this comment.
updateLastLoginAt가 userRepository.getById()로 엔티티를 먼저 조회한 뒤 dirty checking으로 UPDATE를 발생시키므로, 불필요한 SELECT가 추가됩니다. 단순 타임스탬프 갱신이면 @Modifying UPDATE 쿼리로 1쿼리 처리(SELECT 없이)하는 방식이 더 적합합니다.
| return; | ||
| } | ||
|
|
||
| userRepository.getById(userId).updateLastActivityAt(LocalDateTime.now()); | ||
| } |
There was a problem hiding this comment.
updateLastActivityAt도 매 요청마다 getById()로 SELECT 후 UPDATE가 발생합니다. 트래픽이 늘면 users 테이블에 대한 쓰기/락 경합이 커질 수 있으니, @Modifying UPDATE로 조회 없이 갱신하거나(필요 시) 일정 주기 이상 지난 경우에만 갱신하는 방식도 고려해 주세요.
🔍 개요
🚀 주요 변경 내용
users테이블에last_login_at,last_activity_at컬럼을 추가했습니다./refresh호출 시 사용자의last_login_at를 갱신하도록 연동했습니다.인증된 API 요청 이후
last_activity_at를 갱신하는AOP를 추가했습니다.💬 참고 사항
✅ Checklist (완료 조건)