Skip to content

[Feat] 자유기록 종료 시 기록 이름 입력받기 - #318

Merged
JangInho merged 1 commit into
developfrom
feat/#317-free-record-name
Aug 11, 2026
Merged

[Feat] 자유기록 종료 시 기록 이름 입력받기#318
JangInho merged 1 commit into
developfrom
feat/#317-free-record-name

Conversation

@JangInho

@JangInho JangInho commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

🧾 요약

  • 자유기록은 course_id가 null이라 목록 조회에서 courseName이 null로 내려가 이름 자리가 비어 보였습니다. 종료 시 사용자가 이름을 정하도록 받고, 입력하지 않으면 서버가 기본 이름을 만들어 채웁니다.

🔗 이슈

✨ 변경 내용

  • hiking_records.name 컬럼 추가 (V35) - 자유기록에만 채워지고 코스 기록은 null
  • POST /api/tracking/sessions/{sessionId}/complete에 선택적 request body(name) 추가 - completeHikingRecord를 생성하는 유일한 지점이라 여기서 함께 받으면 "이름 없는 기록"이 존재하는 순간이 없음. 별도 PATCH로 붙이면 complete 성공 → PATCH 실패 시 이름 없는 기록이 남음
  • DefaultRecordNameGenerator 추가 - 이름을 비워 보내면 260723_등산왕의코스1 형태로 생성. 표시 이름은 User.displayName()을 재사용 (닉네임은 nullable이고 탈퇴 시 null로 밀리므로 닉네임 → 실명 → "사용자" fallback이 이미 구현되어 있음)
  • 순번은 같은 날 자유기록 수로 계산 - 날짜만으로는 같은 날 두 번 기록했을 때(실수로 종료 후 재시작) 이름이 완전히 겹침. 활성 세션이 유저당 1개라 동시 완료가 불가능해 번호 중복 우려는 없음
  • 이름이 길어질 때 날짜/순번은 보존하고 표시 이름만 잘라냄 - 실명 fallback은 길이 상한이 없는데, 뒤를 자르면 구분자인 순번이 사라짐
  • 목록/상세 응답에 recordName 추가 - 화면 표시는 courseName ?? recordName. courseName에 자유기록 이름을 채우면 courseId는 null인데 courseName은 값이 있는 자기모순 응답이 되고, 상세 응답은 course 객체 자체가 null이라 가짜 CourseSummary를 만들어야 함
  • 코스 기록은 name이 와도 무시 - 자유기록에서 courseId를 무시하는 기존 방향과 맞춤

✅ 확인

  • 빌드 OK
  • 테스트 OK

Summary by CodeRabbit

  • 새 기능
    • 자유 기록 완료 시 사용자가 기록명을 입력할 수 있습니다.
    • 기록명이 없으면 날짜, 사용자명, 순번을 조합한 기본 이름이 자동 생성됩니다.
    • 기록 목록과 상세 화면에서 자유 기록명이 표시됩니다.
    • 기록명은 최대 30자까지 입력할 수 있습니다.
  • 개선
    • 코스 기록에는 입력한 자유 기록명이 적용되지 않습니다.
    • 기록명 생성 시 사용자 정보가 없더라도 대체 이름이 제공됩니다.

자유기록은 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 를 무시하는 것과 같은 방향.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

세션 완료 API가 선택적 기록명을 받습니다. 자유기록은 입력명을 저장하고, 입력이 없으면 서버가 기본 이름을 생성합니다. 코스 기록은 이름을 저장하지 않습니다. 기록 목록과 상세 응답에 recordName을 추가합니다.

Changes

자유기록 이름 기능

Layer / File(s) Summary
기록 이름 계약과 조회 응답
src/main/java/com/semosan/api/domain/hiking/entity/HikingRecord.java, src/main/java/com/semosan/api/domain/hiking/repository/*, src/main/java/com/semosan/api/domain/hiking/dto/response/*
HikingRecordname을 저장합니다. 조회 프로젝션과 응답 DTO가 recordName을 반환합니다. 사용자별 당일 자유기록 수 조회를 추가합니다.
세션 완료와 기본 이름 생성
src/main/java/com/semosan/api/domain/tracking/dto/request/CompleteTrackingSessionRequest.java, src/main/java/com/semosan/api/domain/tracking/controller/*, src/main/java/com/semosan/api/domain/tracking/service/TrackingSessionService.java, src/main/java/com/semosan/api/domain/hiking/policy/DefaultRecordNameGenerator.java
완료 요청은 선택적 name을 받습니다. 자유기록 이름은 trim 후 저장합니다. 이름이 없으면 날짜, 표시 이름, 순번으로 기본 이름을 생성합니다. 코스 기록에서는 요청 이름을 무시합니다.
이름 처리 검증
src/test/java/com/semosan/api/domain/hiking/*, src/test/java/com/semosan/api/domain/tracking/*
엔티티 매핑, 응답 프로젝션, 컨트롤러 전달, 이름 정리, 기본 이름 생성, 코스 기록 처리를 검증합니다.

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: 세션 완료 결과 반환
Loading

Possibly related PRs

  • SEMOSAN/SEMOSAN_BE#54: HikingRecord.fromTrackingSessionTrackingSessionService.complete의 기록명 처리와 직접 연결됩니다.
  • SEMOSAN/SEMOSAN_BE#74: 세션 완료 흐름과 자유기록 이름 저장 변경이 직접 연결됩니다.
  • SEMOSAN/SEMOSAN_BE#122: HikingRecordRepository의 기록 조회 프로젝션 변경과 직접 연결됩니다.

Poem

세션이 닫히면 이름이 피고
빈 이름엔 순번이 길을 내며
코스 기록은 제 이름을 지키고
목록엔 recordName이 반짝인다
산행 한 줄, 이제 길을 잃지 않네.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.30% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive API, 엔티티, 기본 이름 생성, 응답 필드 요구사항은 충족하지만 V35 마이그레이션 파일은 경로 필터로 제외되어 확인할 수 없습니다. 제외된 V35__add_name_to_hiking_records.sql 파일에서 hiking_records.name 컬럼 추가 여부를 확인하세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 자유기록 종료 시 기록 이름 입력 기능이라는 주요 변경 사항을 정확하고 간결하게 설명합니다.
Out of Scope Changes check ✅ Passed 변경 사항과 테스트가 자유기록 이름 저장, 생성, 조회라는 연결 이슈의 범위 안에 있습니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#317-free-record-name

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@JangInho JangInho self-assigned this Aug 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📊 테스트 커버리지 리포트

Overall Project 98.95% 🍏
Files changed 100% 🍏

File Coverage
CompleteTrackingSessionRequest.java 100% 🍏
TrackingSessionController.java 100% 🍏
GetUserHikingRecordResponse.java 100% 🍏
DefaultRecordNameGenerator.java 100% 🍏
HikingRecord.java 100% 🍏
TrackingSessionService.java 98.97% 🍏
HikingRecordDetailResponse.java 98.88% 🍏

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3b7704b and 9908c5d.

⛔ Files ignored due to path filters (1)
  • src/main/resources/db/migration/V35__add_name_to_hiking_records.sql is excluded by !src/main/resources/db/migration/**
📒 Files selected for processing (16)
  • src/main/java/com/semosan/api/domain/hiking/dto/response/GetUserHikingRecordResponse.java
  • src/main/java/com/semosan/api/domain/hiking/dto/response/HikingRecordDetailResponse.java
  • src/main/java/com/semosan/api/domain/hiking/entity/HikingRecord.java
  • src/main/java/com/semosan/api/domain/hiking/policy/DefaultRecordNameGenerator.java
  • src/main/java/com/semosan/api/domain/hiking/repository/HikingRecordRepository.java
  • src/main/java/com/semosan/api/domain/hiking/repository/projection/UserHikingRecordProjection.java
  • src/main/java/com/semosan/api/domain/tracking/controller/TrackingSessionController.java
  • src/main/java/com/semosan/api/domain/tracking/controller/docs/TrackingSessionControllerDocs.java
  • src/main/java/com/semosan/api/domain/tracking/dto/request/CompleteTrackingSessionRequest.java
  • src/main/java/com/semosan/api/domain/tracking/service/TrackingSessionService.java
  • src/test/java/com/semosan/api/domain/hiking/controller/HikingRecordControllerTest.java
  • src/test/java/com/semosan/api/domain/hiking/dto/response/GetUserHikingRecordResponseTest.java
  • src/test/java/com/semosan/api/domain/hiking/entity/HikingRecordTest.java
  • src/test/java/com/semosan/api/domain/hiking/policy/DefaultRecordNameGeneratorTest.java
  • src/test/java/com/semosan/api/domain/tracking/controller/TrackingSessionControllerTest.java
  • src/test/java/com/semosan/api/domain/tracking/service/TrackingSessionServiceTest.java

@JangInho
JangInho merged commit 3454911 into develop Aug 11, 2026
4 checks passed
@howooyeon
howooyeon deleted the feat/#317-free-record-name branch August 11, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 자유기록 종료 시 기록 이름 입력받기

1 participant