[Feat] 자유기록 종료 시 기록 이름 입력받기 - #318
Conversation
자유기록은 course_id 가 null 이라 목록 조회에서 courseName 이 null 로 내려갔고,
프론트 목록에서 이름 자리가 비어 보였다. 종료 시 사용자가 이름을 정하도록 받는다.
- hiking_records.name 컬럼 추가 (V35)
- POST /sessions/{id}/complete 에 선택적 body(name) 추가
- 이름을 비워 보내면 DefaultRecordNameGenerator 가 260723_등산왕의코스1 형태로 생성
- 목록/상세 응답에 recordName 추가 — 화면 표시는 courseName ?? recordName
courses 테이블을 재사용하지 않은 이유: courses 는 코스 추천/산 상세/좋아요에서
전 사용자가 공유하는 마스터 데이터라, 자유기록마다 행을 만들면 남의 코스 목록에
개인 기록 이름이 섞인다.
기본 이름의 순번은 같은 날 자유기록 수로 센다. 날짜만으로는 같은 날 두 번
기록했을 때(실수로 종료 후 재시작하는 경우) 이름이 완전히 겹친다.
코스 기록은 코스명으로 표시되므로 name 이 와도 무시한다 — 자유기록에서
courseId 를 무시하는 것과 같은 방향.
📝 WalkthroughWalkthrough세션 완료 API가 선택적 기록명을 받습니다. 자유기록은 입력명을 저장하고, 입력이 없으면 서버가 기본 이름을 생성합니다. 코스 기록은 이름을 저장하지 않습니다. 기록 목록과 상세 응답에 Changes자유기록 이름 기능
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant TrackingSessionController
participant TrackingSessionService
participant HikingRecordRepository
participant DefaultRecordNameGenerator
Client->>TrackingSessionController: 세션 완료 요청과 선택적 name 전송
TrackingSessionController->>TrackingSessionService: complete(userId, sessionId, name)
TrackingSessionService->>HikingRecordRepository: 당일 자유기록 수 조회
HikingRecordRepository-->>TrackingSessionService: 기존 자유기록 수 반환
TrackingSessionService->>DefaultRecordNameGenerator: 기본 이름 생성 요청
DefaultRecordNameGenerator-->>TrackingSessionService: recordName 반환
TrackingSessionService-->>Client: 세션 완료 결과 반환
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
📊 테스트 커버리지 리포트
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/main/java/com/semosan/api/domain/tracking/service/TrackingSessionService.java (1)
119-140: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift외부 호출을 쓰기 트랜잭션 밖으로 분리하세요.
Line 124의 Redis 통계 조회와 Line 139의 날씨 API 호출이
@Transactional범위 안에서 실행됩니다. 외부 시스템이 지연되면 DB 트랜잭션과 커넥션이 불필요하게 오래 유지됩니다. 통계와 날씨를 트랜잭션 시작 전에 조회하고, 세션 완료·기록 저장·멤버 저장만 별도 트랜잭션 메서드에서 처리하세요.As per path instructions, "
@Transactional범위 안에 외부 API 호출(WebClient 등), Redis 호출 ... 이 섞여 있다면 해당 호출을 트랜잭션 밖으로 분리하거나 트랜잭션을 거는 범위를 좁히도록 제안하라."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/semosan/api/domain/tracking/service/TrackingSessionService.java` around lines 119 - 140, Refactor complete(Long userId, Long sessionId, String requestedName) so Redis statsService.getStats and weatherService.getTemperature execute before any write transaction begins. Move session completion, record creation/persistence, and member persistence into a separate transactional method, passing the pre-fetched stats and optional temperature into it while preserving ownership validation and zero-point warning behavior.Source: Path instructions
src/test/java/com/semosan/api/domain/hiking/entity/HikingRecordTest.java (1)
32-46: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win코스 기록에
name을 저장하는 기대값을 제거하세요.Line 32의
session에는Course가 있습니다. 그런데 이 테스트는 이름을 전달하고 Line 46에서 저장을 기대합니다. 이는 코스 기록에서 입력name을 무시한다는 계약과 충돌합니다.코스 관계를 검증하는 현재 테스트에는
null이름을 전달하세요. 자유기록 이름 저장 검증은course == null인 별도 테스트로 분리하세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/java/com/semosan/api/domain/hiking/entity/HikingRecordTest.java` around lines 32 - 46, Update the HikingRecord.fromTrackingSession test to pass a null name when the session includes a Course, and remove the assertion expecting the course record to retain that name. Add a separate test for the free-record case where course is null that verifies the supplied name is stored.
🧹 Nitpick comments (1)
src/test/java/com/semosan/api/domain/hiking/dto/response/GetUserHikingRecordResponseTest.java (1)
60-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win자유기록의
recordName매핑을 검증하세요.현재 테스트 더블은 항상
null을 반환합니다. 따라서GetUserHikingRecordResponse.from이 자유기록 이름을 누락하거나 잘못 매핑해도 테스트가 통과합니다.recordName이 있는 자유기록 프로젝션을 만들고 응답의recordName()값을 검증하세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/java/com/semosan/api/domain/hiking/dto/response/GetUserHikingRecordResponseTest.java` around lines 60 - 63, Update the test double’s getRecordName() in GetUserHikingRecordResponseTest to return a non-null free-record name, then assert that GetUserHikingRecordResponse.from maps that value to the response recordName().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In
`@src/main/java/com/semosan/api/domain/tracking/service/TrackingSessionService.java`:
- Around line 119-140: Refactor complete(Long userId, Long sessionId, String
requestedName) so Redis statsService.getStats and weatherService.getTemperature
execute before any write transaction begins. Move session completion, record
creation/persistence, and member persistence into a separate transactional
method, passing the pre-fetched stats and optional temperature into it while
preserving ownership validation and zero-point warning behavior.
In `@src/test/java/com/semosan/api/domain/hiking/entity/HikingRecordTest.java`:
- Around line 32-46: Update the HikingRecord.fromTrackingSession test to pass a
null name when the session includes a Course, and remove the assertion expecting
the course record to retain that name. Add a separate test for the free-record
case where course is null that verifies the supplied name is stored.
---
Nitpick comments:
In
`@src/test/java/com/semosan/api/domain/hiking/dto/response/GetUserHikingRecordResponseTest.java`:
- Around line 60-63: Update the test double’s getRecordName() in
GetUserHikingRecordResponseTest to return a non-null free-record name, then
assert that GetUserHikingRecordResponse.from maps that value to the response
recordName().
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 0a83088b-faff-4a9f-81ba-c3b4780d96be
⛔ Files ignored due to path filters (1)
src/main/resources/db/migration/V35__add_name_to_hiking_records.sqlis excluded by!src/main/resources/db/migration/**
📒 Files selected for processing (16)
src/main/java/com/semosan/api/domain/hiking/dto/response/GetUserHikingRecordResponse.javasrc/main/java/com/semosan/api/domain/hiking/dto/response/HikingRecordDetailResponse.javasrc/main/java/com/semosan/api/domain/hiking/entity/HikingRecord.javasrc/main/java/com/semosan/api/domain/hiking/policy/DefaultRecordNameGenerator.javasrc/main/java/com/semosan/api/domain/hiking/repository/HikingRecordRepository.javasrc/main/java/com/semosan/api/domain/hiking/repository/projection/UserHikingRecordProjection.javasrc/main/java/com/semosan/api/domain/tracking/controller/TrackingSessionController.javasrc/main/java/com/semosan/api/domain/tracking/controller/docs/TrackingSessionControllerDocs.javasrc/main/java/com/semosan/api/domain/tracking/dto/request/CompleteTrackingSessionRequest.javasrc/main/java/com/semosan/api/domain/tracking/service/TrackingSessionService.javasrc/test/java/com/semosan/api/domain/hiking/controller/HikingRecordControllerTest.javasrc/test/java/com/semosan/api/domain/hiking/dto/response/GetUserHikingRecordResponseTest.javasrc/test/java/com/semosan/api/domain/hiking/entity/HikingRecordTest.javasrc/test/java/com/semosan/api/domain/hiking/policy/DefaultRecordNameGeneratorTest.javasrc/test/java/com/semosan/api/domain/tracking/controller/TrackingSessionControllerTest.javasrc/test/java/com/semosan/api/domain/tracking/service/TrackingSessionServiceTest.java
🧾 요약
course_id가 null이라 목록 조회에서courseName이 null로 내려가 이름 자리가 비어 보였습니다. 종료 시 사용자가 이름을 정하도록 받고, 입력하지 않으면 서버가 기본 이름을 만들어 채웁니다.🔗 이슈
✨ 변경 내용
hiking_records.name컬럼 추가 (V35) - 자유기록에만 채워지고 코스 기록은 nullPOST /api/tracking/sessions/{sessionId}/complete에 선택적 request body(name) 추가 -complete가HikingRecord를 생성하는 유일한 지점이라 여기서 함께 받으면 "이름 없는 기록"이 존재하는 순간이 없음. 별도 PATCH로 붙이면 complete 성공 → PATCH 실패 시 이름 없는 기록이 남음DefaultRecordNameGenerator추가 - 이름을 비워 보내면260723_등산왕의코스1형태로 생성. 표시 이름은User.displayName()을 재사용 (닉네임은 nullable이고 탈퇴 시 null로 밀리므로 닉네임 → 실명 → "사용자" fallback이 이미 구현되어 있음)recordName추가 - 화면 표시는courseName ?? recordName.courseName에 자유기록 이름을 채우면courseId는 null인데courseName은 값이 있는 자기모순 응답이 되고, 상세 응답은course객체 자체가 null이라 가짜CourseSummary를 만들어야 함name이 와도 무시 - 자유기록에서courseId를 무시하는 기존 방향과 맞춤✅ 확인
Summary by CodeRabbit