Skip to content

[Feat] 약 상세 정보 조회 API + 메모장 자동 생성 기능 추가 - #54

Merged
kangcheolung merged 6 commits into
developfrom
feature/52
Jul 8, 2026
Merged

kangcheolung merged 6 commits into
developfrom
feature/52

Conversation

@kangcheolung

@kangcheolung kangcheolung commented Jul 8, 2026 •

Copy link
Copy Markdown
Member

🔍️ 작업 내용

Closes #52
✨ 상세 설명
약 상세 정보 조회 API 추가

GET /api/medications/{id}/detail 신규 엔드포인트
MedicationQueryService 신규 생성
MedicationDetailResponse 신규 생성 — efcyQesitm(효능), useMethodQesitm(용법), atpnQesitm(주의사항), seQesitm(부작용) 반환
MedicationConverter에 toDetailResponse 변환 메서드 추가

메모장 자동 생성 관련

MedicationCreateRequest에 memo 필드 추가 (복용방법 + 보관방법)
MedicationCommandService에서 memo 저장 처리

기타
DrugInfoController Swagger 설명 보완

🛠️ 추후 리팩토링 및 고도화 계획

memo 자동 생성 로직 고도화 (약물 데이터 존재 여부에 따른 조건부 생성)
📸 스크린샷 (선택)
💬 리뷰 요구사항

Summary by CodeRabbit

  • New Features

    • 약 상세 정보 조회 기능이 추가되어, 특정 약의 상세 내용을 확인할 수 있습니다.
    • 약 등록 시 메모를 함께 저장할 수 있게 되었습니다.
    • 약 정보가 연결되지 않은 경우에도 기본 약명은 표시됩니다.
  • Bug Fixes

    • 일부 약 상세 조회 경로의 접근이 허용되어 더 원활하게 조회할 수 있습니다.
  • Documentation

    • Git 커밋 및 푸시 규칙과 작업 원칙 문서가 추가·갱신되었습니다.

kangcheolung and others added 2 commits July 8, 2026 19:12
- MedicationCreateRequest에 memo 필드 추가 (복용방법 + 보관방법)
- MedicationCommandService에서 memo 저장 처리
- MedicationQueryService 신규 생성 — GET /api/medications/{id}/detail
- MedicationDetailResponse 신규 생성 (efcyQesitm, useMethodQesitm, atpnQesitm, seQesitm)
- MedicationConverter에 toDetailResponse 변환 메서드 추가
- SecurityConfig에 GET /api/medications/** permitAll 추가
- DrugInfoController Swagger 설명 보완

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- .claude/rules/git.md 신규 생성 (globs 없이 항상 로드)
- 커밋 타이밍, 레이어별 분리 순서, 메시지 작성 규칙 포함
- CLAUDE.md에 git.md 참조 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kangcheolung, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9366df40-8b57-48de-93c9-0fec902a8461

📥 Commits

Reviewing files that changed from the base of the PR and between 7a30246 and 0de36dc.

📒 Files selected for processing (4)
  • src/main/java/com/piuda/callcare/domain/medication/controller/MedicationController.java
  • src/main/java/com/piuda/callcare/domain/medication/dto/response/MedicationDetailResponse.java
  • src/main/java/com/piuda/callcare/domain/medication/service/query/MedicationQueryService.java
  • src/main/java/com/piuda/callcare/global/config/SecurityConfig.java
📝 Walkthrough

Walkthrough

약 상세 정보 조회 API(GET /api/medications/{id}/detail)가 신설되어 DrugInfo 연결 여부에 따라 필드를 null 처리하며, MedicationCreateRequest에 memo 필드가 추가되어 등록 시 저장됩니다. SecurityConfig에 해당 경로 permitAll이 추가되고, 문서 파일 및 컨트롤러 설명 문구가 함께 갱신되었습니다.

Changes

약 상세 조회 및 메모 기능

Layer / File(s) Summary
상세 응답 DTO 및 요청 확장
.../dto/response/MedicationDetailResponse.java, .../dto/request/MedicationCreateRequest.java
MedicationDetailResponse record 신설(약품명/효능·효과/용법·용량/주의사항/부작용), MedicationCreateRequest에 memo 필드 추가.
컨버터 및 조회 서비스
.../converter/MedicationConverter.java, .../service/query/MedicationQueryService.java
toDetailResponse가 DrugInfo 유무에 따라 매핑/null 처리, MedicationQueryService.getDetail이 리포지토리 조회 실패 시 CallCareException(MEDICATION_NOT_FOUND) 발생 후 변환 반환.
컨트롤러 엔드포인트 및 보안 설정
.../controller/MedicationController.java, .../global/config/SecurityConfig.java
GET /{medicationId}/detail 엔드포인트 추가 및 MedicationQueryService 주입, SecurityConfig에 해당 GET 경로 permitAll() 추가.
등록 시 memo 저장
.../service/command/MedicationCommandService.java
doRegister에서 Medication 빌더에 request.memo() 값 대입.

Estimated code review effort: 2 (Simple) | ~15 minutes

문서 및 설명 문구 업데이트

Layer / File(s) Summary
Git 규칙 및 문서 갱신
.claude/rules/git.md, CLAUDE.md, .../druginfo/controller/DrugInfoController.java
Git 워크플로우 규칙 문서 신설, CLAUDE.md 룰 자동 로드 목록에 git.md 추가, DrugInfoController의 @Tag 설명 문구 변경.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related PRs

  • PIUDAProject/Backend#45: SecurityConfig의 filterChain에서 동일하게 새 경로를 permitAll()로 확장하는 변경이 겹칩니다.
  • PIUDAProject/Backend#46: 동일한 MedicationController/MedicationCreateRequest/MedicationCommandService 영역을 확장합니다.
  • PIUDAProject/Backend#51: MedicationCreateRequest와 MedicationConverter를 함께 수정하는 동일 도메인 변경입니다.

Suggested labels: ✨ Feature

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning DrugInfo 미연결 시 전체 응답을 null로 반환해야 하는데, 현재는 약 이름만 포함한 DTO를 반환하는 것으로 보입니다. DrugInfo가 없을 때는 MedicationDetailResponse를 만들지 말고 null을 반환하도록 toDetailResponse와 getDetail 로직을 수정하세요.
Out of Scope Changes check ⚠️ Warning 기능 구현과 무관한 .claude 규칙 문서와 CLAUDE.md 자동 로드 수정이 함께 포함되어 있습니다. 기능과 직접 관련 없는 문서·설정 변경은 별도 PR로 분리하고, 이번 PR에는 메모 저장과 상세 조회 구현만 남기세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 약 상세 조회 API와 메모 저장 기능 추가라는 핵심 변경을 잘 요약합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/52

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.

@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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/main/java/com/piuda/callcare/domain/medication/controller/MedicationController.java (1)

20-20: 📐 Maintainability & Code Quality | 🔵 Trivial

@Tag description이 상세 조회 엔드포인트 추가를 반영하지 않습니다

@Tag(description = "약 등록 API")에 상세 조회 기능이 추가되었으므로 설명 업데이트를 권장합니다.

♻️ 제안
-@Tag(name = "Medication", description = "약 등록 API")
+@Tag(name = "Medication", description = "약 등록 및 상세 조회 API")
🤖 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/piuda/callcare/domain/medication/controller/MedicationController.java`
at line 20, Update the Swagger/OpenAPI `@Tag` on MedicationController so its
description reflects both medication registration and the newly added detail 조회
endpoint. Adjust the `@Tag` annotation near MedicationController to use a broader,
accurate description that covers all exposed medication APIs, not just
registration.
🤖 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.

Inline comments:
In
`@src/main/java/com/piuda/callcare/domain/medication/converter/MedicationConverter.java`:
- Around line 16-29: There is a mismatch between the PR goal and
`MedicationConverter.toDetailResponse`: when `drugInfo` is null, the method
currently returns a partially populated `MedicationDetailResponse` with
`drugName`, which conflicts with the stated behavior and the
`MedicationDetailResponse` schema. Please resolve this by choosing one approach
explicitly: either make `toDetailResponse` return null when
`medication.getDrugInfo()` is null, or keep `drugName` populated and update the
`MedicationDetailResponse` schema/PR intent to match that behavior.

In `@src/main/java/com/piuda/callcare/global/config/SecurityConfig.java`:
- Line 47: `GET /api/medications/**` is currently exposed via `permitAll`, which
leaves medication detail lookup vulnerable to IDOR; update `SecurityConfig` to
stop allowing anonymous access here and add the same temporary TODO-style note
used for `/api/home/**` and `/api/conflicts/**` if you need to preserve the
existing pattern. Also make sure `MedicationQueryService.getDetail` and the
corresponding `MedicationController` path enforce `userId`-based ownership
checks so the detail lookup is scoped to the authenticated user rather than a
sequential `medicationId`.

---

Nitpick comments:
In
`@src/main/java/com/piuda/callcare/domain/medication/controller/MedicationController.java`:
- Line 20: Update the Swagger/OpenAPI `@Tag` on MedicationController so its
description reflects both medication registration and the newly added detail 조회
endpoint. Adjust the `@Tag` annotation near MedicationController to use a broader,
accurate description that covers all exposed medication APIs, not just
registration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a3bc016-c1f2-4131-9a12-ca87214671b4

📥 Commits

Reviewing files that changed from the base of the PR and between b83e415 and 7a30246.

📒 Files selected for processing (10)
  • .claude/rules/git.md
  • CLAUDE.md
  • src/main/java/com/piuda/callcare/domain/druginfo/controller/DrugInfoController.java
  • src/main/java/com/piuda/callcare/domain/medication/controller/MedicationController.java
  • src/main/java/com/piuda/callcare/domain/medication/converter/MedicationConverter.java
  • src/main/java/com/piuda/callcare/domain/medication/dto/request/MedicationCreateRequest.java
  • src/main/java/com/piuda/callcare/domain/medication/dto/response/MedicationDetailResponse.java
  • src/main/java/com/piuda/callcare/domain/medication/service/command/MedicationCommandService.java
  • src/main/java/com/piuda/callcare/domain/medication/service/query/MedicationQueryService.java
  • src/main/java/com/piuda/callcare/global/config/SecurityConfig.java

Comment on lines +16 to +29
// Medication → MedicationDetailResponse (약 상세 조회용, DrugInfo 없으면 전 필드 null)
public MedicationDetailResponse toDetailResponse(Medication medication) {
DrugInfo drugInfo = medication.getDrugInfo();
if (drugInfo == null) {
return new MedicationDetailResponse(medication.getDrugName(), null, null, null, null);
}
return new MedicationDetailResponse(
medication.getDrugName(),
drugInfo.getEfcyQesitm(),
drugInfo.getUseMethodQesitm(),
drugInfo.getAtpnQesitm(),
drugInfo.getSeQesitm()
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

PR 목표와 구현이 불일치합니다 — DrugInfo 미연결 시 응답 처리 방식 확인 필요

PR 목표는 "DrugInfo가 연결되지 않은 경우 전체 상세 응답을 null로 반환"이라고 명시하지만, 현재 구현은 drugName을 포함한 non-null 응답을 반환합니다. 또한 MedicationDetailResponse의 @Schema 설명인 "DrugInfo 미연결 시 전 필드 null"과도 일치하지 않습니다 (drugName이 설정됨).

두 가지 해석이 가능합니다:

  1. PR 목표대로 drugInfo == null일 때 null 반환
  2. 현재 구현대로 drugName은 유지하되 Schema/PR 목표 업데이트
💡 옵션 1: PR 목표에 맞추는 경우
     if (drugInfo == null) {
-        return new MedicationDetailResponse(medication.getDrugName(), null, null, null, null);
+        return null;
     }
💡 옵션 2: 현재 구현을 유지하는 경우 — Schema 설명 수정
-@Schema(description = "약 상세 정보 응답 — DrugInfo 미연결 시 전 필드 null")
+@Schema(description = "약 상세 정보 응답 — DrugInfo 미연결 시 DrugInfo 관련 필드는 null, drugName은 Medication에서 반환")

Based on learnings, if multiple interpretations of a task exist, present the alternatives rather than choosing silently.

🤖 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/piuda/callcare/domain/medication/converter/MedicationConverter.java`
around lines 16 - 29, There is a mismatch between the PR goal and
`MedicationConverter.toDetailResponse`: when `drugInfo` is null, the method
currently returns a partially populated `MedicationDetailResponse` with
`drugName`, which conflicts with the stated behavior and the
`MedicationDetailResponse` schema. Please resolve this by choosing one approach
explicitly: either make `toDetailResponse` return null when
`medication.getDrugInfo()` is null, or keep `drugName` populated and update the
`MedicationDetailResponse` schema/PR intent to match that behavior.

Source: Learnings

.requestMatchers(HttpMethod.POST, "/api/conflicts/**").permitAll()
.requestMatchers(HttpMethod.POST, "/api/ocr/**").permitAll()
.requestMatchers(HttpMethod.POST, "/api/medications/batch").permitAll()
.requestMatchers(HttpMethod.GET, "/api/medications/**").permitAll()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

GET /api/medications/** permitAll — IDOR 위험 및 TODO 주석 누락

GET /api/medications/**가 permitAll로 설정되어 인증 없이 모든 약 상세 정보에 접근 가능합니다. medicationId가 순차적 DB ID이므로 열거 공격으로 타 사용자의 약 정보(drugName 등)가 노출됩니다.

/api/home/**, /api/conflicts/**에는 인증 필터 도입 시 제거하겠다는 TODO 주석이 있지만, medications에는 누락되어 있습니다. 또한 MedicationQueryService.getDetail에 userId 기반 소유권 검증이 없어 인증 도입 시에도 IDOR이 해결되지 않습니다.

🔒 권장 사항

1. TODO 주석 추가 (즉시, 기존 패턴 일관성):

+			// TODO: 인증 필터 도입 시 제거하고, 로그인 사용자의 seniorId 소유권 검증으로 전환
 			.requestMatchers(HttpMethod.GET, "/api/medications/**").permitAll()

2. 인증 도입 시 소유권 검증 추가 (MedicationQueryService + MedicationController):

 // MedicationController
 public ResponseEntity<ApiResponse<MedicationDetailResponse>> getDetail(
+        `@AuthenticationPrincipal` Long userId,
         `@PathVariable` Long medicationId
 ) {
-    return ResponseUtils.ok(medicationQueryService.getDetail(medicationId));
+    return ResponseUtils.ok(medicationQueryService.getDetail(medicationId, userId));
 }
 // MedicationQueryService
-public MedicationDetailResponse getDetail(Long medicationId) {
+public MedicationDetailResponse getDetail(Long medicationId, Long userId) {
     Medication medication = medicationRepository.findById(medicationId)
             .orElseThrow(() -> new CallCareException(ErrorCode.MEDICATION_NOT_FOUND));
+    if (!medication.getSenior().getUser().getId().equals(userId)) {
+        throw new CallCareException(ErrorCode.FORBIDDEN);
+    }
     return medicationConverter.toDetailResponse(medication);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.requestMatchers(HttpMethod.GET, "/api/medications/**").permitAll()
// TODO: 인증 필터 도입 시 제거하고, 로그인 사용자의 seniorId 소유권 검증으로 전환
.requestMatchers(HttpMethod.GET, "/api/medications/**").permitAll()
🤖 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/piuda/callcare/global/config/SecurityConfig.java` at line
47, `GET /api/medications/**` is currently exposed via `permitAll`, which leaves
medication detail lookup vulnerable to IDOR; update `SecurityConfig` to stop
allowing anonymous access here and add the same temporary TODO-style note used
for `/api/home/**` and `/api/conflicts/**` if you need to preserve the existing
pattern. Also make sure `MedicationQueryService.getDetail` and the corresponding
`MedicationController` path enforce `userId`-based ownership checks so the
detail lookup is scoped to the authenticated user rather than a sequential
`medicationId`.

kangcheolung and others added 4 commits July 8, 2026 19:39
DrugInfo 미연결 시 의약품 정보 필드만 null, drugName은 항상 반환

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
userId가 non-null인 경우 medication → senior → user 경로로 소유권 검증
null이면 로컬 테스트용으로 통과 처리 (permitAll 유지 구간)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
userId를 service로 전달하여 소유권 검증 수행

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 메모장 자동 생성 및 약 상세 정보 조회 API 구현

1 participant