Skip to content

feat: 감정일기 수정 , 조회 api 추가 - #113

Merged
SG-Develope merged 6 commits into
devfrom
feature/calendar-daily-record-api
Jun 23, 2026
Merged

SG-Develope merged 6 commits into
devfrom
feature/calendar-daily-record-api

Conversation

@SG-Develope

@SG-Develope SG-Develope commented Jun 22, 2026

Copy link
Copy Markdown
Contributor
  • 감정일기 조회 수정기능 추가밑 공통 컴포넌트 헤더 추가

Summary by CodeRabbit

릴리스 노트

  • 신기능

    • 캘린더 페이지에 로고 헤더 추가
    • 일기 상세/편집/삭제 플로우 정비
  • 개선사항

    • 일기 생성·수정 폼 통합 및 로딩/중복 제출 처리 강화
    • 일기 상세 페이지 레이아웃(캐릭터/여백/간격) 개선
    • 하단 네비게이션에서 ‘일기’ 메뉴 경로 이동 최적화

@SG-Develope SG-Develope self-assigned this Jun 22, 2026
@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
talky-owl Ready Ready Preview, Comment Jun 23, 2026 12:38am

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

일기 CRUD 전체 흐름을 구현한다. Prisma 서비스 계층(createDiary/updateDiaryById/deleteDiaryById/getDiaryById), API 엔드포인트(POST/GET/PATCH/DELETE), 클라이언트 API 함수 및 React Query 훅(useCreateDiary/useUpdateDiary/useDeleteDiary)을 추가하고, 일기 상세 페이지를 서버 컴포넌트로 전환하며, 수정 전용 페이지와 수정/삭제 액션 컴포넌트를 신규 추가한다. 캘린더 페이지 헤더 추가 및 네비게이션 경로 조정도 포함된다.

Changes

일기 CRUD 및 상세 페이지 전환

Layer / File(s) Summary
DiaryDetail 타입 및 서비스 계층 CRUD
src/types/diary.ts, src/domains/diary/diary.service.ts
DiaryDetail 인터페이스를 추가하고, Prisma 기반 createDiary/updateDiaryById/deleteDiaryById/getDiaryById 서비스 함수를 구현한다. soft-delete 및 소유권 검증, 날짜 포매팅과 nullable 필드 처리 포함.
일기 API 엔드포인트 (GET/POST/PATCH/DELETE)
src/app/api/diary/route.ts, src/app/api/diary/[diaryId]/route.ts, src/app/api/calendar/route.ts
POST /api/diary 신규 추가로 일기 생성, /api/diary/[diaryId]에 GET/PATCH/DELETE 핸들러 구현으로 조회/수정/삭제 수행. 기존 GET 엔드포인트들의 인증 방식을 getServerSession 기반으로 전환.
클라이언트 API 함수 및 React Query 훅
src/domains/diary/diary.api.ts, src/domains/diary/diary.hooks.ts
createDiary/updateDiary/deleteDiary API 클라이언트 함수와 useCreateDiary/useUpdateDiary/useDeleteDiary mutation 훅을 추가한다. 성공 시 쿼리 무효화 및 라우팅 수행.
DiaryCreate 생성/수정 공통 폼 컴포넌트
src/app/(page)/diary/create/page.tsx, src/app/(page)/diary/create/page.module.scss
DiaryCreatemode/diary props 기반 공통 생성·수정 폼으로 확장한다. diary 객체 기반 상태 초기화, 뮤테이션 분기, 로딩 오버레이 스피너, 헤더 타이틀 전환 포함.
일기 상세 서버 컴포넌트 전환 및 액션
src/app/(page)/diary/[id]/page.tsx, src/app/(page)/diary/[id]/page.module.scss, src/app/(page)/diary/[id]/DiaryDetailHeader.tsx, src/app/(page)/diary/[id]/DiaryActions.tsx, src/app/(page)/diary/[id]/DiaryActions.module.scss, src/app/(page)/diary/[id]/edit/page.tsx
상세 페이지를 서버 컴포넌트로 전환하고 DiaryDetailHeader, DiaryActions(수정/삭제 모달) 클라이언트 컴포넌트를 신규 추가한다. /diary/[id]/edit 수정 전용 서버 페이지도 새로 구현.
캘린더 헤더·네비게이션·레이아웃 조정
src/app/(page)/calendar/page.tsx, src/app/(page)/calendar/page.module.scss, src/components/layout/BottomNavigation.tsx, src/components/calendar/RecordList.module.scss, src/components/diary/EmotionDiaryList.module.scss, src/components/layout/Header.tsx
캘린더 페이지에 Header(variant=logo) 추가, BottomNavigation 일기 메뉴 href를 /calendar로 변경, 하단 여백·패딩 값 조정, 로고 클릭 시 홈 경로로 이동 기능 추가.
Disputes API 필터 정리 및 코드 정리
src/app/api/disputes/route.ts, src/domains/dispute/dispute.api.ts, src/domains/dispute/dispute.hooks.ts
Disputes GET API에서 completed 상태 필터 분기 제거 및 page/limit 파라미터 재배치. import 순서 및 표현식 괄호 등 코드 정리 포함.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DiaryDetailPage as /diary/[id]<br/>(server)
  participant DiaryActions as DiaryActions<br/>(client)
  participant DiaryAPI as /api/diary/[diaryId]
  participant DiaryEditPage as /diary/[id]/edit<br/>(server)
  participant DiaryCreateForm as DiaryCreate<br/>(mode=edit)

  rect rgba(70, 130, 180, 0.5)
    note over User, DiaryDetailPage: 일기 상세 조회
    User->>DiaryDetailPage: 페이지 접근
    DiaryDetailPage->>DiaryDetailPage: getServerSession + getDiaryById
    DiaryDetailPage-->>User: DiaryDetailHeader + DiaryActions 렌더
  end

  rect rgba(60, 179, 113, 0.5)
    note over User, DiaryCreateForm: 일기 수정 흐름
    User->>DiaryActions: 수정 버튼 클릭
    DiaryActions->>DiaryEditPage: router.push(/diary/[id]/edit)
    DiaryEditPage->>DiaryEditPage: getServerSession + getDiaryById
    DiaryEditPage-->>DiaryCreateForm: mode="edit" + diary 데이터
    User->>DiaryCreateForm: 폼 수정 및 제출
    DiaryCreateForm->>DiaryAPI: PATCH { title, content, emotionType }
    DiaryAPI-->>DiaryCreateForm: 200 { success }
    DiaryCreateForm-->>User: /diary/[id]로 이동
  end

  rect rgba(220, 80, 80, 0.5)
    note over User, DiaryAPI: 일기 삭제 흐름
    User->>DiaryActions: 삭제 버튼 클릭
    DiaryActions->>DiaryActions: 삭제 확인 모달 표시
    User->>DiaryActions: 삭제 확인 버튼
    DiaryActions->>DiaryAPI: DELETE /api/diary/[diaryId]
    DiaryAPI-->>DiaryActions: 200 { success }
    DiaryActions-->>User: /calendar로 이동
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

  • I5-Project/TALKY-OWL#41: 동일한 src/app/(page)/calendar/page.tsx 렌더링 로직 및 .fab 여백 관련 page.module.scss 수정
  • I5-Project/TALKY-OWL#81: 본 PR의 src/app/(page)/diary/create/page.tsx 생성 폼 및 감정 모드 CategoryFilter 작업 도입
  • I5-Project/TALKY-OWL#98: 본 PR이 인증 방식을 전환하는 GET /api/calendarGET /api/diary 엔드포인트 초기 구현

Suggested reviewers

  • juahcheon
  • lyla-bae

🐇 일기를 쓰고, 고치고, 지우는 날,
토끼가 달력 위를 폴짝 뛰어다녔네.
수정 버튼 누르면 모달이 짠~ 🎉
서버는 세션 확인, 서비스는 꼼꼼히,
오늘도 감정일기 무사히 저장됐다 🌸

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning PR 설명이 매우 간단하며 필수 섹션 대부분이 미작성되어 있습니다. 작업 내용, 담당 영역, 테스트 결과, 작업 범위 확인 등 필수 정보가 부족합니다. PR 템플릿의 모든 섹션을 작성하세요. 특히 작업 내용, 담당 영역 선택, 테스트 결과 확인, 보안 검증 항목을 상세히 기입하십시오.
Docstring Coverage ⚠️ Warning Docstring coverage is 13.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 '감정일기 수정, 조회 api 추가'로 변경 사항의 핵심(감정일기 API 추가)을 명확히 반영하고 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/calendar-daily-record-api

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/app/api/disputes/route.ts (2)

86-129: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

completed=true 필터 제거로 완료 목록 API 계약이 깨졌습니다.

이 변경 이후 completed=true가 더 이상 완료 상태 필터링을 수행하지 않아, 완료 사건 목록 조회가 의도와 다르게 동작합니다. 현재 클라이언트는 여전히 completed=true로 요청하고 있어(완료 목록 훅/레코드 목록 경로), 결과 데이터 정합성이 깨집니다. completed 분기를 복원하거나, 최소한 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/app/api/disputes/route.ts` around lines 86 - 129, The `completed` query
parameter filtering has been removed from the where clause construction, causing
the API contract to break since clients still send `completed=true` requests for
completed dispute lists. Restore the handling for the completed parameter in the
where clause by adding a conditional that filters for completed statuses (such
as DisputeStatus.JUDGED, DisputeStatus.CLOSED, DisputeStatus.EXPIRED, etc.) when
the completed parameter is true, similar to how the active and rawStatus
parameters are currently handled in the spread operator pattern.

83-84: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

page/limit 숫자 파싱 실패 시 NaN이 그대로 전파됩니다.

parseInt가 실패하면 NaN이 되고, 현재 보정식으로는 정상 숫자로 복구되지 않습니다. 이 값이 skip/take로 들어가면 조회가 500으로 실패할 수 있습니다.

제안 수정안
-  const page = Math.max(1, parseInt(searchParams.get('page') ?? '1', 10))
-  const limit = Math.min(50, Math.max(1, parseInt(searchParams.get('limit') ?? '20', 10)))
+  const parsedPage = Number.parseInt(searchParams.get('page') ?? '1', 10)
+  const parsedLimit = Number.parseInt(searchParams.get('limit') ?? '20', 10)
+  const page = Number.isFinite(parsedPage) ? Math.max(1, parsedPage) : 1
+  const limit = Number.isFinite(parsedLimit) ? Math.min(50, Math.max(1, parsedLimit)) : 20
🤖 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/app/api/disputes/route.ts` around lines 83 - 84, The parseInt operations
for page and limit parameters can return NaN when parsing fails, and the current
Math.max/Math.min operations do not properly handle NaN values since Math
operations with NaN return NaN. Add explicit validation using isNaN after each
parseInt call to ensure the parsed values are valid numbers, and provide
appropriate fallback values (1 for page and 20 for limit) when parsing fails or
results in NaN. This will prevent NaN from being passed to the skip and take
query parameters which would cause database query failures.
🤖 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/app/`(page)/diary/create/page.tsx:
- Around line 37-38: The current implementation on line 37 uses new
Date().toISOString().slice(0, 10) which calculates the date in UTC time,
potentially causing a one-day offset from the user's local date. This affects
how the diary entry is stored and retrieved in calendar operations. Replace this
UTC-based date calculation with one that uses the user's local timezone by
extracting the year, month, and day from a Date object using local time methods
(getFullYear, getMonth, getDate) and formatting them as YYYY-MM-DD, ensuring the
diaryDate sent to createMutation.mutate reflects the user's actual local date.

In `@src/app/api/diary/`[diaryId]/route.ts:
- Around line 75-107: The PATCH handler (and DELETE handler as noted) lacks
exception handling for potential failures during request.json() parsing and
service operations. Wrap the entire body of the PATCH function in a try-catch
block to handle two error scenarios: catch SyntaxError when request.json() fails
and return a 400 VALIDATION_ERROR response, and catch any other errors that
occur during updateDiaryById execution and return a 500 INTERNAL_SERVER_ERROR
response. Apply the same try-catch pattern to the DELETE handler to ensure
consistent error handling and guarantee all errors return properly formatted
ApiResponse objects.

In `@src/app/api/diary/route.ts`:
- Line 108: The catch block at line 108 in the GET handler is silently consuming
errors without any logging, making it impossible to debug failures in
production. Add error logging inside this catch block that captures the actual
error details and includes the route prefix (e.g., "GET /api/diary") to enable
operational traceability and incident diagnosis. This aligns with the logging
MVP requirements for API error logs and DB failure logs.
- Around line 11-23: The POST handler lacks exception handling around the
request.json() call and body destructuring at lines 21-22, which can throw
uncaught exceptions that don't follow the common ApiResponse format. Wrap the
body parsing (request.json()) and destructuring (extracting title, content,
emotionType, diaryDate) in a try-catch block, then return appropriate
ApiResponse formatted error responses for both JSON parsing failures and
validation errors, ensuring all error paths return consistent ApiResponse
structure with proper status codes.
- Around line 24-32: The validation check for diaryDate in the POST route (the
if statement checking !content and !diaryDate) only verifies that the field
exists, but does not validate the date format. Add format validation for
diaryDate to ensure it matches the YYYY-MM-DD format and passes a canonical
check (matching the validation logic used in the GET endpoint). If the diaryDate
format is invalid, return a 400 response with a VALIDATION_ERROR code in the
same manner as the existing validation error response, ensuring that malformed
date inputs are caught at the validation layer rather than escalating to 500
errors in the service layer.

In `@src/domains/diary/diary.service.ts`:
- Around line 24-40: The updateDiaryById function uses a check-then-act pattern
that is not atomic and loses failure cause information. Instead of performing a
separate findUnique query and then updating by id alone, consolidate all
conditions (id, userId, and deletedAt: null) into a single atomic update
operation in the prisma.emotionDiary.update call. Check the count of affected
records from the update result to determine success. Replace the boolean return
type with an enum that distinguishes between three outcomes: success, not found
(404), and unauthorized (403). Apply the same atomic update pattern and enum
return type to the delete operation as well (mentioned to also apply at lines
49-67).

In `@src/types/diary.ts`:
- Line 59: The emotionType field in the DiaryDetail type is currently defined as
string | null, which allows any string value to be passed through the edit flow
without validation. Change the type of the emotionType property in the
DiaryDetail interface from string | null to EmotionType | null to enforce that
only valid emotion type values can be assigned at compile time. This will
prevent invalid emotion strings from being retained or resent during the diary
edit operations referenced in the create page.

---

Outside diff comments:
In `@src/app/api/disputes/route.ts`:
- Around line 86-129: The `completed` query parameter filtering has been removed
from the where clause construction, causing the API contract to break since
clients still send `completed=true` requests for completed dispute lists.
Restore the handling for the completed parameter in the where clause by adding a
conditional that filters for completed statuses (such as DisputeStatus.JUDGED,
DisputeStatus.CLOSED, DisputeStatus.EXPIRED, etc.) when the completed parameter
is true, similar to how the active and rawStatus parameters are currently
handled in the spread operator pattern.
- Around line 83-84: The parseInt operations for page and limit parameters can
return NaN when parsing fails, and the current Math.max/Math.min operations do
not properly handle NaN values since Math operations with NaN return NaN. Add
explicit validation using isNaN after each parseInt call to ensure the parsed
values are valid numbers, and provide appropriate fallback values (1 for page
and 20 for limit) when parsing fails or results in NaN. This will prevent NaN
from being passed to the skip and take query parameters which would cause
database query failures.
🪄 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: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 39c725d0-77f5-412c-9b46-e81b29280cea

📥 Commits

Reviewing files that changed from the base of the PR and between 72a7d57 and c877697.

📒 Files selected for processing (23)
  • src/app/(page)/calendar/page.module.scss
  • src/app/(page)/calendar/page.tsx
  • src/app/(page)/diary/[id]/DiaryActions.module.scss
  • src/app/(page)/diary/[id]/DiaryActions.tsx
  • src/app/(page)/diary/[id]/DiaryDetailHeader.tsx
  • src/app/(page)/diary/[id]/edit/page.tsx
  • src/app/(page)/diary/[id]/page.module.scss
  • src/app/(page)/diary/[id]/page.tsx
  • src/app/(page)/diary/create/page.module.scss
  • src/app/(page)/diary/create/page.tsx
  • src/app/api/calendar/route.ts
  • src/app/api/diary/[diaryId]/route.ts
  • src/app/api/diary/route.ts
  • src/app/api/disputes/route.ts
  • src/components/calendar/RecordList.module.scss
  • src/components/diary/EmotionDiaryList.module.scss
  • src/components/layout/BottomNavigation.tsx
  • src/domains/diary/diary.api.ts
  • src/domains/diary/diary.hooks.ts
  • src/domains/diary/diary.service.ts
  • src/domains/dispute/dispute.api.ts
  • src/domains/dispute/dispute.hooks.ts
  • src/types/diary.ts

Comment thread src/app/(page)/diary/create/page.tsx
Comment thread src/app/api/diary/[diaryId]/route.ts Outdated
Comment thread src/app/api/diary/route.ts
Comment thread src/app/api/diary/route.ts Outdated
Comment thread src/app/api/diary/route.ts Outdated
Comment thread src/domains/diary/diary.service.ts Outdated
Comment thread src/types/diary.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (2)
src/components/layout/Header.tsx (1)

32-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

접근성 향상을 위해 Link에 aria-label 추가를 고려하세요.

로고 이미지를 Link로 감싼 구현은 정상적으로 동작하지만, 스크린 리더 사용자를 위해 Link 컴포넌트에 명시적인 aria-label을 추가하면 링크의 목적을 더 명확하게 전달할 수 있습니다.

♿ 접근성 개선 제안
-          <Link href="/">
+          <Link href="/" aria-label="홈으로 이동">
             <Image
               src="/images/common/logo.svg"
               alt="말해부엉"
🤖 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/components/layout/Header.tsx` around lines 32 - 40, The Link component
wrapping the logo image lacks an aria-label attribute, which reduces
accessibility for screen reader users. Add an aria-label prop to the Link
component (which has href="/") with a descriptive label that communicates the
purpose of the link, such as navigating to the home page. This will help screen
reader users understand that clicking the logo returns them to the homepage.
src/domains/diary/diary.service.ts (1)

30-30: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

emotionType 입력 타입을 도메인 유니언으로 좁혀 주세요.

src/types/diary.ts의 허용값 계약이 있는데 여기서는 string으로 열려 있어 잘못된 감정값이 서비스 경계까지 들어올 수 있습니다.

수정 예시
-  data: { title: string; content: string; emotionType: string },
+  data: { title: string; content: string; emotionType: EmotionType },
🤖 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/domains/diary/diary.service.ts` at line 30, The emotionType field in the
data parameter of the diary service method is currently typed as a generic
string, which allows invalid emotion values. Import the emotion type union from
src/types/diary.ts and replace the emotionType string type with the proper
domain union type to ensure only valid emotion values are accepted at the
service boundary.
🤖 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/app/api/diary/route.ts`:
- Around line 66-67: In the catch block of the diary creation route (the POST
endpoint in src/app/api/diary/route.ts), replace the direct console.error(error)
call that logs the raw error object with a safer logging approach. Instead of
logging the entire error object which may contain sensitive diary data or
personal information, construct a log message that includes only the route
prefix (like the API endpoint path) and non-sensitive metadata about the error
(such as error type or code). This prevents sensitive data like diary original
text or personal information from being exposed in logs while still providing
useful debugging information.
- Around line 56-57: The emotionType parameter received from the client is
passed directly to the createDiary function without validation against the
EmotionType contract, which can result in invalid values being stored or causing
runtime errors. Before calling createDiary in the try block, validate that the
emotionType value is one of the allowed EmotionType values (not just applying a
default fallback). If the provided emotionType is invalid, either return an
appropriate error response to the client or validate it against a whitelist of
valid emotion types to ensure only valid values are persisted to the database.

---

Nitpick comments:
In `@src/components/layout/Header.tsx`:
- Around line 32-40: The Link component wrapping the logo image lacks an
aria-label attribute, which reduces accessibility for screen reader users. Add
an aria-label prop to the Link component (which has href="/") with a descriptive
label that communicates the purpose of the link, such as navigating to the home
page. This will help screen reader users understand that clicking the logo
returns them to the homepage.

In `@src/domains/diary/diary.service.ts`:
- Line 30: The emotionType field in the data parameter of the diary service
method is currently typed as a generic string, which allows invalid emotion
values. Import the emotion type union from src/types/diary.ts and replace the
emotionType string type with the proper domain union type to ensure only valid
emotion values are accepted at the service boundary.
🪄 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: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a9a72b8-bb4d-4ed7-bcfb-748f4aff34e1

📥 Commits

Reviewing files that changed from the base of the PR and between c877697 and 5d0617b.

📒 Files selected for processing (5)
  • src/app/api/diary/[diaryId]/route.ts
  • src/app/api/diary/route.ts
  • src/components/layout/Header.tsx
  • src/domains/diary/diary.service.ts
  • src/types/diary.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/types/diary.ts
  • src/app/api/diary/[diaryId]/route.ts

Comment on lines +56 to +57
try {
const diaryId = await createDiary(userId, { title: title ?? '', content, emotionType: emotionType ?? 'neutral', diaryDate });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

emotionType 허용값을 검증한 뒤 저장해 주세요.

현재는 클라이언트 문자열을 그대로 전달하므로 EmotionType 계약 밖의 값이 500으로 승격되거나 잘못 저장될 수 있습니다.

수정 예시
-import type { DiaryItem } from '`@/types/diary`';
+import type { DiaryItem, EmotionType } from '`@/types/diary`';
+
+const EMOTION_TYPES: readonly EmotionType[] = ['happy', 'sad', 'angry', 'annoyed', 'neutral'];
+const isEmotionType = (value: unknown): value is EmotionType =>
+  typeof value === 'string' && EMOTION_TYPES.includes(value as EmotionType);
+  const normalizedEmotionType = emotionType ?? 'neutral';
+  if (!isEmotionType(normalizedEmotionType)) {
+    return NextResponse.json<ApiResponse>(
+      { success: false, error: { code: 'VALIDATION_ERROR', message: 'emotionType 값이 올바르지 않습니다.' } },
+      { status: 400 },
+    );
+  }
+
   try {
-    const diaryId = await createDiary(userId, { title: title ?? '', content, emotionType: emotionType ?? 'neutral', diaryDate });
+    const diaryId = await createDiary(userId, { title: title ?? '', content, emotionType: normalizedEmotionType, diaryDate });
🤖 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/app/api/diary/route.ts` around lines 56 - 57, The emotionType parameter
received from the client is passed directly to the createDiary function without
validation against the EmotionType contract, which can result in invalid values
being stored or causing runtime errors. Before calling createDiary in the try
block, validate that the emotionType value is one of the allowed EmotionType
values (not just applying a default fallback). If the provided emotionType is
invalid, either return an appropriate error response to the client or validate
it against a whitelist of valid emotion types to ensure only valid values are
persisted to the database.

Comment on lines +66 to +67
} catch (error) {
console.error(error);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

POST 오류 로그에서 raw error 객체를 그대로 남기지 마세요.

일기 생성 경로는 민감한 원문을 다루므로 예외 객체 전체 대신 route prefix와 비민감 메타데이터만 남기는 편이 안전합니다.

수정 예시
   } catch (error) {
-    console.error(error);
+    console.error('[POST /api/diary]', {
+      name: error instanceof Error ? error.name : 'UnknownError',
+      type: typeof error,
+    });

As per coding guidelines, src/app/api/**/*.{ts,tsx}Do not store unnecessary case original text, diary original text, or personal information in logs를 요구합니다.

📝 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
} catch (error) {
console.error(error);
} catch (error) {
console.error('[POST /api/diary]', {
name: error instanceof Error ? error.name : 'UnknownError',
type: typeof error,
});
🤖 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/app/api/diary/route.ts` around lines 66 - 67, In the catch block of the
diary creation route (the POST endpoint in src/app/api/diary/route.ts), replace
the direct console.error(error) call that logs the raw error object with a safer
logging approach. Instead of logging the entire error object which may contain
sensitive diary data or personal information, construct a log message that
includes only the route prefix (like the API endpoint path) and non-sensitive
metadata about the error (such as error type or code). This prevents sensitive
data like diary original text or personal information from being exposed in logs
while still providing useful debugging information.

Source: Coding guidelines

@SG-Develope
SG-Develope merged commit bfd1cf0 into dev Jun 23, 2026
3 checks passed
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.

1 participant