Skip to content

Conversation

@lemoncurdyogurt
Copy link
Member

@lemoncurdyogurt lemoncurdyogurt commented Jan 6, 2026

🔥 작업 내용

  • 하루한컷 업로드 단계에서 시작단계 ui
  • 하루한컷 미션 단계에 따른 타이틀 및 버튼 텍스트 상수화
  • global.css 중복되는 코드 제거 및 gradient-orange 컬러 추가
  • 현재 요일에 따른 과거, 현재, 미래 판단하는 유틸함수 추가
  • 미션 완료와 현재요일 상태에 따른 미션진행 상황 피드 ui 조건분기

🤔 추후 작업 사항

  • 하루한컷 미션카드 확정되는대로 아이콘 및 텍스트 상수화 처리
  • 이미지 업로드 유틸 추가
  • 이미지업로드 후 단계별 ui 추가

📸 작업 내역 스크린샷

2026-01-07.4.35.46.mov

🔗 이슈

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 일일 미션 카드·주간 진행 UI 추가 및 사진 업로드 페이지 추가
    • 공유 일기용 댓글 페이지 및 댓글 목록 기능 추가
  • 개선 사항
    • 메시지 입력: IME 지원, 포커스 감지 및 엔터 전송 개선
    • 전송 버튼에 클릭 동작 추가
    • 헤더 제목 색상 옵션 추가 및 빈 자리(플러스) 아바타에 업로드로 이동하는 클릭 동작 추가
  • 스타일
    • 전역 스타일 및 그라데이션 유틸리티 조정
  • 유틸리티
    • 댓글 날짜 표시 포맷 추가

✏️ Tip: You can customize this high-level summary in your review settings.

신수진 added 23 commits January 6, 2026 17:25
@lemoncurdyogurt lemoncurdyogurt requested a review from idid10 January 6, 2026 19:40
@lemoncurdyogurt lemoncurdyogurt added the feat 기능 추가 label Jan 6, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 6, 2026

Walkthrough

일일 미션 카드/주간 진행률, 공유 일기 댓글(모달/페이지) 리팩토링, MessageInput/SendButton/BackHeader Props 확장, 여러 유틸·타입·라우팅 및 의존성(@toss/use-funnel@use-funnel/next) 변경이 포함된 기능/UI 추가 및 구조 변경입니다.

Changes

Cohort / File(s) 요약
의존성
package.json
@toss/use-funnel ^1.4.2 → @use-funnel/next ^0.0.22
일일 미션 관련
src/components/dailyPhoto/*, src/app/day-story/upload/page.tsx, src/constants/missionCard.ts, src/constants/dayToKorean.ts, src/utils/getCurrentDay.ts
신규 DailyMissionCard, DailyMissionProgress 컴포넌트 및 업로드 페이지 추가, 미션 상태·요일 상수·유틸 도입
공유 일기 댓글 시스템
src/components/dailyRecord/SharedDiaryChat.tsx, src/components/dailyRecord/CommentList.tsx, src/app/shared-diary/[id]/comment/page.tsx, src/app/shared-diary/upload/page.tsx, src/mock/diaryComment.json, src/types/diaryComment.type.ts, src/utils/formatDate.ts
SharedDiaryChat → SharedDiaryComment(variant: modal
입력/헤더/버튼 Props 강화
src/components/common/MessageInput.tsx, src/assets/SendButton.tsx, src/components/common/BackHeader.tsx
MessageInput: onFocus/onSend, IME 조합 처리, SendButton: onClick prop 추가, BackHeader: titleColor prop 추가
일일기록 관련 라우팅/ID 변경
src/app/day-log/page.tsx, src/components/dailyRecord/MissionFeed.tsx, src/components/dailyRecord/MissionInfo.tsx, src/constants/navBarItems.ts
MY_ID 101→111 변경, 빈 아바타 클릭시 /day-story/upload 네비게이션 추가, NAV_ITEMS "/record""/day-log"
UI/스타일·기타
src/components/dailyRecord/Header.tsx, src/components/dailyRecord/ShareDiaryItem.tsx, src/styles/globals.css
헤더 텍스트 클래스 추가, SharedDiaryChat 참조 → SharedDiaryComment로 교체, 글로벌 스타일에 bg-gradient-orange 등 CSS 변경

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant UI as UI (페이지/모달)
    participant Shared as SharedDiaryComment
    participant CommentList as CommentList
    participant MessageInput as MessageInput
    participant Router as Router
    participant State as Comments State

    User->>UI: 공유 일기 열기 (모달 또는 페이지)
    UI->>Shared: render(variant)
    Shared->>CommentList: props.comments 전달
    CommentList->>User: 댓글 렌더링

    User->>MessageInput: 입력 필드 포커스
    MessageInput->>Router: onFocus 시 page variant로 이동 (모달일 때)
    Router-->>UI: 네비게이션

    User->>MessageInput: 메시지 입력 (IME 포함)
    MessageInput->>MessageInput: composition events 처리
    MessageInput->>MessageInput: Enter 또는 전송 버튼 -> sendMessage
    MessageInput->>State: 새 댓글 추가
    State->>CommentList: 업데이트된 comments
    CommentList->>User: 새 댓글 렌더링

    rect rgb(230,245,255)
      Note over Shared,MessageInput: Modal/Page 공통 로직 및 상태 공유
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

🐰✨ 미션을 한 줄씩 채워가며
체크 마크로 한 주를 반짝이고
댓글은 모달에서 페이지로 뛰어넘어
키보드와 IME를 부드럽게 안아주네
한 컷, 한 이야기, 함께 완성해요 🥕🎞️

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 '하루한컷 업로드' 기능 추가에 관한 것으로, 실제 변경 사항의 핵심 목표와 일치합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec28cdb and 0494d6c.

📒 Files selected for processing (1)
  • src/components/dailyPhoto/DailyMissionCard.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/dailyPhoto/DailyMissionCard.tsx

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Fix all issues with AI Agents
In @package.json:
- Line 20: The package.json entry for "@use-funnel/next" references a
non-existent v0.0.22 and may be incompatible with our React/Next versions;
change the dependency to the published v0.0.21 (or verify v0.0.22 exists) and
then audit the codebase for the library's breaking changes: replace array step
definitions with createFunnelSteps(), swap any <Funnel> / <Funnel.Step> usage to
funnel.Render or conditional rendering, migrate state HOC usage of withState()
to createFunnelSteps generic types, and replace onStepChange callbacks with
useEffect reading funnel.step; also confirm our React/Next versions match the
library's supported versions before committing the dependency change.

In @src/app/day-log/page.tsx:
- Line 10: The hardcoded MY_ID = 111 in src/app/day-log/page.tsx (also
duplicated in src/components/dailyRecord/MissionFeed.tsx) is dev-only mock data;
refactor by removing the duplicate constant and either (a) extract a shared
constant (e.g., export const DEV_MOCK_USER_ID) in a single module and import it
where needed for local testing, or (b) prefer passing userId as a prop from the
parent (page.tsx) into MissionFeed, and replace MY_ID usage with that prop;
additionally prepare for production by replacing the mock with a call to your
auth/context provider (e.g., getUserIdFromAuth or useAuth().user.id) so runtime
derives the real user id when authentication is available.

In @src/app/shared-diary/upload/page.tsx:
- Line 1: The file src/app/shared-diary/upload/page.tsx is an empty placeholder
and will render a blank page; either implement the page component (export
default async function SharedDiaryUploadPage() { ... }) with the form and
handlers, or replace the file contents with a simple placeholder UI (e.g., a
"Coming Soon / Under Construction" React component) or disable the route by
removing its export; pick one approach, add basic accessibility and skeleton
markup, and open an issue referencing "shared-diary upload page implementation"
if you want to defer full work.

In @src/components/common/BackHeader.tsx:
- Line 30: BackHeader uses a dynamic Tailwind class `text-${titleColor}` which
JIT won't generate; replace the dynamic string with a deterministic class
mapping or conditional rendering: create a small map or switch keyed by the
titleColor prop (e.g., map titleColor values to 'text-white', 'text-black',
etc.) and apply the resulting class to BackIcon and the other affected element
instead of `text-${titleColor}`, or alternatively render conditional classNames
for known values to ensure Tailwind generates the CSS.

In @src/components/dailyPhoto/DailyMissionCard.tsx:
- Line 35: In DailyMissionCard update the conditional className expression to
use strict equality (replace `==` with `===`) when comparing the `status`
variable so the ternary `status === "NOT_STARTED" ? "bg-gradient-orange" :
"bg-green-01"` is used, ensuring type-safe comparison in the JSX expression that
builds the className string.

In @src/components/dailyRecord/SharedDiaryChat.tsx:
- Around line 26-27: Validate the route id from useParams before using
Number(id): ensure id exists and parses to a finite integer (e.g.,
parseInt/Number + Number.isFinite/Number.isInteger and > 0) and only assign
numericId when valid; if invalid, short-circuit the component (return null or an
error UI) or perform a safe redirect (using your routing navigate) instead of
proceeding to the routing logic that expects a number — update the numericId
assignment and add the guard around any code paths that use numericId
(references: useParams, id, numericId).

In @src/utils/formatDate.ts:
- Around line 1-8: formatDate currently creates a Date from createdAt without
validation, producing "aN년 aN월aN일" for invalid inputs; update the formatDate
function to validate the Date (e.g., const date = new Date(createdAt); if
(isNaN(date.getTime())) { ... }) and handle invalid cases by either throwing a
clear error, returning a safe fallback string (like "Invalid date" or ""), or
accepting an optional default value; ensure all downstream callers can handle
the chosen fallback and keep the returned type string.
🧹 Nitpick comments (13)
src/mock/diaryComment.json (1)

1-23: 목 데이터의 댓글 내용 다양화 필요.

이미지 파일(/images/1.jpg, /images/2.jpg, /images/3.jpg)은 실제로 public/images/ 디렉토리에 존재합니다. 그러나 댓글 내용이 모두 "코코넛 커피도 마셔주세요"로 동일하며 숫자만 추가된 형태입니다. 더 현실적인 테스트를 위해 댓글 내용을 다양하게 작성하는 것을 권장합니다.

src/assets/SendButton.tsx (1)

9-16: 접근성 개선 제안: disabled 상태 추가

현재 hasText가 false일 때 cursor-pointer만 제거하고 버튼은 여전히 클릭 가능합니다. MessageInput.tsx에서 가드 처리하고 있지만, 더 나은 접근성과 의미론을 위해 hasText가 false일 때 버튼에 disabled 속성을 추가하는 것을 고려해보세요.

🔎 제안하는 개선 방안
 export const SendButton = ({ hasText = false, onClick }: SendButtonProps) => {
   return (
     <button
       className="absolute top-1 right-4 rotate-[-23deg] px-[2px] py-[3px]"
       onClick={onClick}
+      disabled={!hasText}
     >
       <SendIcon
-        className={`text-neutral-04 h-[18px] w-[20px] ${hasText ? "cursor-pointer" : ""}`}
+        className={`h-[18px] w-[20px] ${hasText ? "text-neutral-04 cursor-pointer" : "text-neutral-07"}`}
       />
     </button>
   );
 };
src/components/dailyRecord/MissionFeed.tsx (2)

42-47: 접근성 개선 필요: 클릭 가능한 div에 키보드 접근성이 없습니다.

현재 divonClick만 있어 키보드 사용자가 접근할 수 없습니다. 접근성을 위해 role, tabIndex, onKeyDown 추가를 권장합니다.

🔎 접근성 개선 제안
-            <div
-              className="bg-neutral-11 flex h-[88px] w-[88px] items-center justify-center rounded-full"
-              onClick={() => router.push("/day-story/upload")}
-            >
+            <div
+              role="button"
+              tabIndex={0}
+              className="bg-neutral-11 flex h-[88px] w-[88px] items-center justify-center rounded-full"
+              onClick={() => router.push("/day-story/upload")}
+              onKeyDown={(e) => {
+                if (e.key === "Enter" || e.key === " ") {
+                  router.push("/day-story/upload");
+                }
+              }}
+            >

11-11: 하드코딩된 MY_ID 관련 참고사항.

MY_ID가 컴포넌트 내부에 하드코딩되어 있습니다. TODO 주석(Line 16)에서 언급된 대로 API 연결 시 인증 상태 관리(zustand 등)에서 가져오도록 개선이 필요합니다.

src/app/day-story/upload/page.tsx (1)

8-8: 레이아웃 안정성을 위해 min-height 추가 고려.

bg-black 배경이 뷰포트 전체를 채우도록 min-h-screen을 추가하면 콘텐츠가 적을 때도 일관된 레이아웃을 유지할 수 있습니다.

🔎 제안된 수정
-    <main className="w-full bg-black">
+    <main className="min-h-screen w-full bg-black">
src/app/shared-diary/[id]/comment/page.tsx (1)

1-1: 파일명과 컴포넌트명 불일치 참고.

SharedDiaryChat.tsx 파일에서 SharedDiaryComment를 export하고 있어 명명 규칙에 일관성이 없습니다. 유지보수성을 위해 파일명을 SharedDiaryComment.tsx로 변경하거나, export 이름을 파일명과 맞추는 것을 고려해 보세요.

src/components/dailyRecord/MissionInfo.tsx (1)

29-31: 접근성 개선 필요: 클릭 가능한 div에 키보드 접근성이 없습니다.

MissionFeed.tsx와 동일하게, 클릭 가능한 영역에 키보드 접근성을 추가하는 것이 좋습니다.

🔎 접근성 개선 제안
         <div
-          className="flex h-[76px] cursor-pointer items-center justify-between p-[10px]"
+          role="button"
+          tabIndex={0}
+          className="flex h-[76px] cursor-pointer items-center justify-between p-[10px]"
           onClick={() => router.push("/day-story/upload")}
+          onKeyDown={(e) => {
+            if (e.key === "Enter" || e.key === " ") {
+              router.push("/day-story/upload");
+            }
+          }}
         >
src/constants/missionCard.ts (1)

4-4: subtitle 타입 일관성 개선 고려.

NOT_STARTED.subtitle은 함수이고 다른 상태는 문자열이라 소비 측에서 typeof 체크가 필요합니다. 모두 함수로 통일하거나, 별도 필드로 분리하면 타입 안정성이 향상됩니다.

🔎 타입 일관성 개선 예시
// 옵션 1: 모든 subtitle을 함수로 통일
export const MISSION_STATUS = {
  NOT_STARTED: {
    title: "하루한컷 미션 도착!",
    subtitle: (localday: string) => `${localday}요일`,
    buttonText: "시작하기",
  },
  IMAGE_UPLOADED: {
    title: "하루한컷 미션을 공유할까요?",
    subtitle: () => "확인을 누르면 미션이 완료돼요",
    buttonText: "완료하기",
  },
  IMAGE_CONFIRMED: {
    title: "미션이 완료되었어요",
    subtitle: () => "소중한 오늘의 한컷을 기록했어요",
    buttonText: "확인",
  },
} as const;
src/components/dailyPhoto/DailyMissionCard.tsx (2)

10-11: 사용되지 않는 상태 setter를 확인하세요.

setStatus가 선언되었지만 컴포넌트 내에서 사용되지 않고 있습니다. 향후 미션 상태 변경 기능을 구현할 예정이라면 TODO 코멘트를 추가하거나, 현재 사용하지 않는다면 상태 관리를 제거하고 const status = "NOT_STARTED"로 단순화하는 것을 고려해보세요.


34-38: 버튼에 이벤트 핸들러 추가를 고려하세요.

버튼이 cursor-pointer 스타일을 가지고 있지만 onClick 핸들러가 없습니다. 향후 작업에서 버튼 클릭 시 상태 변경 또는 페이지 이동 등의 기능을 추가할 예정이라면, TODO 주석을 남겨두는 것이 좋습니다.

src/components/dailyPhoto/DailyMissionProgress.tsx (1)

15-15: 하드코딩된 값을 동적 데이터로 교체할 계획을 확인하세요.

isTodayCompleted가 현재 false로 하드코딩되어 있습니다. 향후 실제 미션 완료 상태를 반영하도록 props로 전달받거나 API에서 가져오는 방식으로 개선이 필요합니다. PR 설명의 "추후 작업" 섹션에 이 내용이 포함되어 있는지 확인해주세요.

src/components/dailyRecord/SharedDiaryChat.tsx (2)

5-6: React 훅 임포트를 하나로 통합할 수 있습니다.

가독성을 위해 useStateuseEffect를 한 줄로 임포트하는 것을 권장합니다.

🔎 제안하는 수정
-import { useState } from "react";
-import { useEffect } from "react";
+import { useState, useEffect } from "react";

85-96: 새 댓글 ID 생성 방식을 개선하는 것을 고려하세요.

Date.now()를 ID로 사용하는 것은 임시 솔루션으로는 적절하지만, 실제 API 연동 시 서버에서 생성된 ID를 사용하도록 리팩토링이 필요합니다. 또한 MyProfile 데이터의 존재 여부를 검증하는 것도 고려해보세요.

💡 참고사항

현재는 로컬 상태에만 댓글이 추가되므로, 추후 API 연동 시 다음을 고려하세요:

  • 서버에서 반환된 ID 사용
  • 낙관적 업데이트(optimistic update) 패턴 적용
  • 에러 처리 및 롤백 로직
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d458a3e and ec28cdb.

⛔ Files ignored due to path filters (3)
  • src/assets/left-arrow.svg is excluded by !**/*.svg
  • src/assets/questionMark.svg is excluded by !**/*.svg
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (24)
  • package.json
  • src/app/day-log/page.tsx
  • src/app/day-story/upload/page.tsx
  • src/app/shared-diary/[id]/comment/page.tsx
  • src/app/shared-diary/upload/page.tsx
  • src/assets/SendButton.tsx
  • src/components/common/BackHeader.tsx
  • src/components/common/MessageInput.tsx
  • src/components/dailyPhoto/DailyMissionCard.tsx
  • src/components/dailyPhoto/DailyMissionProgress.tsx
  • src/components/dailyRecord/CommentList.tsx
  • src/components/dailyRecord/Header.tsx
  • src/components/dailyRecord/MissionFeed.tsx
  • src/components/dailyRecord/MissionInfo.tsx
  • src/components/dailyRecord/ShareDiaryItem.tsx
  • src/components/dailyRecord/SharedDiaryChat.tsx
  • src/constants/dayToKorean.ts
  • src/constants/missionCard.ts
  • src/constants/navBarItems.ts
  • src/mock/diaryComment.json
  • src/styles/globals.css
  • src/types/diaryComment.type.ts
  • src/utils/formatDate.ts
  • src/utils/getCurrentDay.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-02T13:08:01.441Z
Learnt from: lemoncurdyogurt
Repo: IT-Cotato/12th-SimTok-FE PR: 3
File: src/utils/formatPhone.ts:6-9
Timestamp: 2026-01-02T13:08:01.441Z
Learning: In phone number formatting/validation logic, enforce that Korean numbers are valid only as 10-digit landlines or 11-digit mobile numbers starting with 010. Do not perform strict intermediate-length formatting for 7–9 digits during input; assume intermediate values are not considered valid until they reach 10 or 11 digits. Implement or update validation to accept only 10 or 11 digits (with 010 prefix for mobiles) and, if formatting is needed during input, preserve user-friendly partial handling without emitting strict validation errors for intermediate states.

Applied to files:

  • src/utils/formatDate.ts
  • src/utils/getCurrentDay.ts
🧬 Code graph analysis (7)
src/components/dailyPhoto/DailyMissionCard.tsx (3)
src/constants/missionCard.ts (1)
  • MISSION_STATUS (1-17)
src/utils/getCurrentDay.ts (1)
  • getTodayIndex (5-8)
src/constants/dayToKorean.ts (1)
  • WEEK_DAYS (1-1)
src/app/shared-diary/[id]/comment/page.tsx (1)
src/components/dailyRecord/SharedDiaryChat.tsx (1)
  • SharedDiaryComment (21-102)
src/app/day-story/upload/page.tsx (3)
src/components/common/BackHeader.tsx (1)
  • BackHeader (15-49)
src/components/dailyPhoto/DailyMissionProgress.tsx (1)
  • DailyMissionProgress (10-49)
src/components/dailyPhoto/DailyMissionCard.tsx (1)
  • DailyMissionCard (9-41)
src/components/dailyRecord/SharedDiaryChat.tsx (3)
src/types/myProfile.type.ts (1)
  • MyProfile (1-6)
src/components/dailyRecord/CommentList.tsx (1)
  • CommentList (11-45)
src/components/common/MessageInput.tsx (1)
  • MessageInput (10-41)
src/components/dailyRecord/ShareDiaryItem.tsx (1)
src/components/dailyRecord/SharedDiaryChat.tsx (1)
  • SharedDiaryComment (21-102)
src/components/dailyRecord/CommentList.tsx (2)
src/types/diaryComment.type.ts (1)
  • DiaryComment (1-7)
src/utils/formatDate.ts (1)
  • formatDate (1-8)
src/components/common/MessageInput.tsx (1)
src/assets/SendButton.tsx (1)
  • SendButton (7-18)
🔇 Additional comments (27)
src/components/dailyRecord/Header.tsx (1)

16-16: LGTM!

명시적인 Tailwind 스타일링이 추가되어 헤더 텍스트의 일관성이 개선되었습니다.

src/constants/navBarItems.ts (1)

25-25: 라우트 변경 검증 완료 - 문제 없음.

새로운 /day-log 라우트가 src/app/day-log/page.tsx에 정상적으로 존재하며, 기존 /record 경로에 대한 참조는 코드베이스에서 완전히 제거되었습니다. navBarItems.ts의 "record" 키는 단순 식별자이며 실제 경로(href)는 /day-log로 올바르게 설정되어 있습니다.

src/styles/globals.css (3)

44-44: 색상 토큰 값 정리 확인됨

16진수 색상 코드의 대소문자는 동일하게 작동하므로 이 변경은 코드 일관성을 위한 정리입니다.


48-90: AI 요약과 실제 코드의 불일치

AI 요약에서는 scrollbar-hidebg-radial-yellowgreen-mintgreen 클래스가 제거되었다고 하지만, 제공된 코드(48-67번째 줄)에는 여전히 존재합니다. 나머지 gradient 클래스들의 포맷팅 변경은 일관성을 위한 것으로 보입니다.


91-93: 새로운 gradient 유틸리티 추가 확인됨

하루한컷 업로드 기능을 위한 오렌지 그라데이션 유틸리티가 추가되었습니다. 구현이 명확하고 적절합니다.

src/components/common/MessageInput.tsx (4)

5-8: Props 인터페이스 정의 확인됨

MessageInputProps 인터페이스가 명확하게 정의되었습니다. 옵셔널 props로 설계되어 유연한 사용이 가능합니다.


12-12: IME 조합 처리 구현 확인됨

한글과 같은 조합형 문자 입력을 위한 isComposing 상태 관리가 잘 구현되었습니다. onCompositionStart, onCompositionEnd, onKeyDown 이벤트를 적절히 활용하여 조합 중에는 Enter 키로 전송되지 않도록 방지합니다. 이는 국제화된 입력 처리의 모범 사례입니다.

Also applies to: 29-36


15-19: 메시지 전송 로직 확인됨

sendMessage 함수가 적절하게 구현되었습니다. 빈 텍스트 검증, 공백 제거(trim), 전송 후 입력 필드 초기화가 모두 올바르게 처리됩니다.


24-24: 입력 필드 UX 개선 확인됨

포커스 시 민트색 테두리를 표시하여 시각적 피드백을 제공하고, onFocus 이벤트를 상위 컴포넌트로 전달하여 유연한 동작(예: 전체 페이지로 이동)을 지원합니다. SendButton과의 연동도 적절히 구현되었습니다.

Also applies to: 28-28, 38-38

src/assets/SendButton.tsx (1)

5-5: onClick prop 추가 확인됨

SendButtononClick 핸들러가 추가되어 클릭 동작을 외부에서 제어할 수 있게 되었습니다. 타입 정의가 올바르고 구현이 적절합니다.

Also applies to: 7-7, 11-11

src/components/dailyRecord/ShareDiaryItem.tsx (1)

16-16: 컴포넌트 이름 변경 확인됨

SharedDiaryChatSharedDiaryComment로 이름이 변경되었습니다. 이는 컴포넌트의 실제 기능(댓글 표시)을 더 정확하게 반영하는 의미론적 개선입니다.

Also applies to: 92-92

src/app/day-story/upload/page.tsx (1)

6-16: LGTM! 페이지 구조가 잘 구성되어 있습니다.

Server Component로 적절히 구현되었고, 클라이언트 컴포넌트들을 올바르게 합성하여 사용하고 있습니다.

src/types/diaryComment.type.ts (1)

1-7: LGTM! 타입 정의가 명확합니다.

DiaryComment 타입이 필요한 필드를 잘 정의하고 있으며, mock 데이터 및 CommentList 컴포넌트와 일관성 있게 사용됩니다.

src/app/shared-diary/[id]/comment/page.tsx (1)

3-5: LGTM! 페이지 컴포넌트 구조가 적절합니다.

variant prop을 활용하여 modal/page 분기를 잘 처리하고 있습니다.

src/components/dailyRecord/MissionInfo.tsx (1)

12-40: 조건부 렌더링 구조가 명확합니다.

hasMyRecord 상태에 따른 UI 분기가 잘 구현되어 있고, 미완료 상태에서 업로드 페이지로의 네비게이션이 적절합니다.

src/constants/missionCard.ts (1)

1-17: LGTM! 미션 상태 상수가 잘 정의되어 있습니다.

as const를 사용하여 타입 안정성을 확보했습니다.

src/constants/dayToKorean.ts (1)

1-1: WEEK_DAYS 상수 정의가 적절합니다!

as const를 사용하여 리터럴 타입 추론이 올바르게 적용되었습니다. 배열이 월요일(index 0)부터 시작하는 것에 대해 우려할 필요는 없습니다. getCurrentDay.tsgetMondayStartIndex 함수가 JavaScript의 getDay()(일요일 = 0) 결과를 WEEK_DAYS 배열의 인덱싱(월요일 = 0)으로 정확하게 변환하고 있습니다:

(day === 0 ? 6 : day - 1)

이 변환으로 Sunday(0) → 6, Monday(1) → 0이 되어 완벽하게 매핑됩니다.

src/components/dailyPhoto/DailyMissionCard.tsx (1)

15-18: LGTM!

subtitle 계산 로직이 함수형과 문자열형 subtitle을 모두 올바르게 처리하고 있습니다. 함수인 경우 "요일" 접미사를 추가하여 완전한 요일 표현을 생성하는 것이 적절합니다.

src/components/dailyPhoto/DailyMissionProgress.tsx (1)

17-46: LGTM!

체크 아이콘 표시 로직과 상태별 스타일링이 올바르게 구현되었습니다. 과거 요일과 완료된 오늘 요일에 체크 표시를, 미래 요일과 미완료된 오늘 요일에 물음표를 표시하는 로직이 명확하고 정확합니다.

src/utils/getCurrentDay.ts (1)

1-18: LGTM!

요일 인덱스 변환 및 상태 판단 로직이 정확하게 구현되었습니다. getMondayStartIndex는 JavaScript의 일요일 시작(0) 인덱싱을 월요일 시작(0) 인덱싱으로 올바르게 변환하고, getWeekDayStatus는 대상 인덱스와 오늘 인덱스를 비교하여 과거/현재/미래를 정확하게 판단합니다.

src/components/dailyRecord/CommentList.tsx (3)

1-9: LGTM!

임포트 구조와 타입 정의가 명확하고 적절합니다.


11-19: LGTM!

빈 상태 처리가 사용자 친화적이고 적절합니다.


25-31: 외부 이미지 URL은 현재 코드에서 사용되지 않습니다. 실제 데이터는 /images/1.jpg와 같은 상대 경로를 사용하므로 remotePatterns 설정이 필요하지 않습니다. Next.js Image 컴포넌트는 public 디렉토리의 상대 경로를 기본적으로 지원합니다.

Likely an incorrect or invalid review comment.

src/components/dailyRecord/SharedDiaryChat.tsx (4)

32-40: LGTM!

모달 모드에서 배경 스크롤을 방지하는 로직이 올바르게 구현되어 있으며, 클린업 함수도 적절합니다.


43-52: LGTM!

모달과 페이지 모드에 따른 조건부 렌더링과 이벤트 처리가 적절하게 구현되어 있습니다.


53-69: LGTM!

섹션 레이아웃과 CommentList 통합이 깔끔하게 구현되어 있습니다.


72-78: LGTM!

이미지 크기 속성과 className이 일치하여 올바르게 설정되어 있습니다.

},
"dependencies": {
"@toss/use-funnel": "^1.4.2",
"@use-funnel/next": "^0.0.22",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

@use-funnel/next version 0.0.22 compatibility Next.js 16 React 19

💡 Result:

Short answer: No — there’s no published @use-funnel/next v0.0.22 on npm, and the package’s docs list supported React as ^16.8, so the current release is not explicitly compatible with React 19 / Next.js 16. [1][2][3]

Sources:

  • @use-funnel/next npm (latest published 0.0.21). [1]
  • @use-funnel docs — Supported React Version: ^16.8. [2]
  • Package index (Socket/npm metadata) showing latest version 0.0.21 (no 0.0.22). [3]

If you want, I can: 1) check the package’s GitHub for any unreleased v0.0.22/branches or compatibility notes, or 2) suggest a migration/workaround (shim/adapter) for using it with Next.js 16 + React 19. Which would you prefer?


🏁 Script executed:

# Check package.json for Next.js and React versions
find . -name "package.json" -type f | head -1 | xargs cat | head -50

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 192


🌐 Web query:

@toss/use-funnel vs @use-funnel/next differences API migration

💡 Result:

Summary of key differences and migration steps from @toss/use-funnel → @use-funnel (/@use-funnel/next):

What changed (high-level)

  • New package family: @use-funnel (core + adapters like @use-funnel/next). @toss/use-funnel is deprecated. [1][2]
  • No hard Next.js dependency: core is router-agnostic and supports Next App/Pages, react-router, React Native, custom routers via adapters. [3]
  • Stronger typing for per-step context: steps are defined with createFunnelSteps() so each step can have its own state type. [1][6]
  • API shape: useFunnel now takes an options object and returns a funnel controller (funnel.step, funnel.context, funnel.history, funnel.Render) rather than the old tuple + /<Funnel.Step/> components. [1][6]
  • History + state are integrated: history snapshots include state so back/forward restores prior step-specific state. [4]

Concrete migration mappings

  • Step list → createFunnelSteps

    • Before: useFunnel(['A','B'] as const)
    • After: const steps = createFunnelSteps().extends(['A','B']).build(); useFunnel({ steps, initial: { step: 'A', context: {} } }). [1]
    • This lets you pass generics for per-step context (replaces withState()). [1]
  • Components → render by step or funnel.Render

    • Before: returned with <Funnel.Step name="A">…
    • After: switch on funnel.step and render, or use funnel.Render component. [1]
  • withState() → type generics on createFunnelSteps/useFunnel

    • Before: .withState<...> HOC pattern
    • After: define state types with createFunnelSteps generics and supply initial context in useFunnel options. [1]
  • onStepChange callback → observe funnel.step

    • Before: onStepChange option passed to useFunnel
    • After: watch funnel.step in useEffect (or use funnel history hooks). [1]
  • Initial step via router query

    • Example: read router.query and pass as initial.step when calling useFunnel (adapter-specific examples in the Next adapter docs). [1]

Migration checklist (minimal)

  1. Install @use-funnel/next (or appropriate adapter). [1]
  2. Replace create of steps with createFunnelSteps().extends(...).build(). Add generics for per-step context if needed. [1]
  3. Replace useFunnel([...]) calls with useFunnel({ id?, steps, initial: { step, context } }). [6]
  4. Replace /<Funnel.Step> UI with switch(funnel.step) or funnel.Render. [1]
  5. Move any withState logic into step-specific types/initial context. [1]
  6. Replace onStepChange option with useEffect(() => {/* react to funnel.step */}, [funnel.step]). [1]
  7. Verify history behavior (back/forward) works as expected; adjust router adapter/custom router if you used non-Next routing. [4][3]

References

  • Migration guide / docs (examples & breaking changes). [1]
  • useFunnel API docs (options/returned object). [6]
  • Background / rationale and router-agnostic redesign (Toss engineering post). [3]
  • Features: history + state integration description. [4]

If you want, tell me one example file from your repo (old code) and I’ll produce the exact migrated code to @use-funnel/next.


버전 번호 확인 및 호환성 검증 필수.

npm에 게시된 @use-funnel/next의 최신 버전은 v0.0.21이며, v0.0.22는 존재하지 않습니다. 또한 공식 문서에서 지원하는 React 버전은 ^16.8로 명시되어 있어 React 19와의 명시적 호환성이 보장되지 않습니다.

@toss/use-funnel에서 @use-funnel/next로의 마이그레이션에는 다음과 같은 주요 breaking changes가 있습니다:

  • Step 정의 방식 변경 (배열 → createFunnelSteps() 사용)
  • 컴포넌트 구조 변경 (<Funnel>/<Funnel.Step>funnel.Render 또는 조건부 렌더링)
  • 상태 관리 API 변경 (withState() HOC → createFunnelSteps 제네릭 타입)
  • 콜백 패턴 변경 (onStepChangeuseEffect with funnel.step)

다음을 반드시 확인하세요:

  • 버전을 v0.0.21로 수정하거나 v0.0.22 존재 여부 재확인
  • 프로젝트의 React 및 Next.js 버전이 실제 호환되는지 검증
  • 위 API 변경 사항들이 코드에 모두 반영되었는지 검토
🤖 Prompt for AI Agents
In @package.json at line 20, The package.json entry for "@use-funnel/next"
references a non-existent v0.0.22 and may be incompatible with our React/Next
versions; change the dependency to the published v0.0.21 (or verify v0.0.22
exists) and then audit the codebase for the library's breaking changes: replace
array step definitions with createFunnelSteps(), swap any <Funnel> /
<Funnel.Step> usage to funnel.Render or conditional rendering, migrate state HOC
usage of withState() to createFunnelSteps generic types, and replace
onStepChange callbacks with useEffect reading funnel.step; also confirm our
React/Next versions match the library's supported versions before committing the
dependency change.


const DailyRecordPage = () => {
const MY_ID = 101;
const MY_ID = 111;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if MY_ID is used elsewhere in the codebase
rg -n "MY_ID" --type=ts --type=tsx

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 95


🏁 Script executed:

# Examine the full context of the page.tsx file
cat -n src/app/day-log/page.tsx

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 1074


🏁 Script executed:

# Search for authentication-related patterns and user context
rg -n "useAuth|useSession|getCurrentUser|getUser|userId" --type=ts --type=tsx -A 2 | head -50

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 95


🏁 Script executed:

# Check if there's a git history showing the change from 101 to 111
git log -p --all -S "MY_ID" -- "src/app/day-log/page.tsx" 2>/dev/null | head -100

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

#!/bin/bash
# Search for MY_ID with general pattern matching
rg "MY_ID" -g "*.ts" -g "*.tsx" --type-list | head -20

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 457


🏁 Script executed:

# Try without file type restriction
rg -n "MY_ID" --include="*.ts" --include="*.tsx"

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 509


🏁 Script executed:

# Search for authentication/user context patterns
rg -n "(useAuth|useSession|getCurrentUser|getUser|userId|authContext)" --include="*.ts" --include="*.tsx" | head -40

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 509


🏁 Script executed:

# Check the mock data file to understand the structure
cat -n src/mock/dailyRecord.json | head -50

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 1352


🏁 Script executed:

# Search for other hardcoded IDs to understand the testing pattern
rg -n "= (10[0-9]|11[0-9])" --include="*.ts" --include="*.tsx" | grep -i "id\|user"

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 509


🏁 Script executed:

#!/bin/bash
# Simple search for MY_ID usage
rg "MY_ID" .

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 480


🏁 Script executed:

# Check if userId 111 exists in the full mock data file
cat src/mock/dailyRecord.json | grep -n "111"

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

# Check the full length of the mock data to see all userIds
tail -20 src/mock/dailyRecord.json

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 423


🏁 Script executed:

# Search for authentication/context patterns more simply
rg "useAuth\|useSession\|AuthContext\|UserContext" .

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 50


🏁 Script executed:

# Look at the git history of the dailyRecord file
git log --oneline src/mock/dailyRecord.json 2>/dev/null | head -10

Repository: IT-Cotato/12th-SimTok-FE

Length of output: 92


하드코딩된 MY_ID는 개발용 모의 데이터입니다. 코드 중복 제거 및 인증 통합 준비 필요

MY_ID = 111은 모의 데이터와 함께 사용되는 개발 단계의 코드입니다. 실제로 dailyRecord.json의 모든 userId는 101~110 범위이므로, 111은 "레코드 없음" 시나리오를 의도적으로 테스트하는 것으로 확인됩니다.

다만, 다음 두 가지를 개선해야 합니다:

  1. 코드 중복: MY_ID = 111이 src/app/day-log/page.tsxsrc/components/dailyRecord/MissionFeed.tsx에서 반복되고 있습니다. 공용 상수로 추출하거나 상위 컴포넌트에서 props로 전달하는 것을 권장합니다.

  2. 프로덕션 준비: 현재 인증 시스템이 없으므로, 프로덕션 배포 전에 실제 인증 컨텍스트에서 사용자 ID를 가져오도록 리팩토링해야 합니다.

🤖 Prompt for AI Agents
In @src/app/day-log/page.tsx at line 10, The hardcoded MY_ID = 111 in
src/app/day-log/page.tsx (also duplicated in
src/components/dailyRecord/MissionFeed.tsx) is dev-only mock data; refactor by
removing the duplicate constant and either (a) extract a shared constant (e.g.,
export const DEV_MOCK_USER_ID) in a single module and import it where needed for
local testing, or (b) prefer passing userId as a prop from the parent (page.tsx)
into MissionFeed, and replace MY_ID usage with that prop; additionally prepare
for production by replacing the mock with a call to your auth/context provider
(e.g., getUserIdFromAuth or useAuth().user.id) so runtime derives the real user
id when authentication is available.

@@ -0,0 +1 @@
//공유일기 글쓰기 페이지
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

미구현 페이지 추적 필요.

이 파일은 주석만 있고 실제 구현이 없는 플레이스홀더입니다. 사용자가 이 라우트에 접근하면 빈 페이지가 표시될 수 있습니다.

다음 중 하나를 권장합니다:

  • 페이지 구현을 완료하거나
  • 임시로 "준비 중" UI를 표시하거나
  • 라우트를 비활성화

해당 작업을 위한 이슈를 생성하거나 기본 페이지 구조를 생성하는 데 도움이 필요하신가요?

🤖 Prompt for AI Agents
In @src/app/shared-diary/upload/page.tsx at line 1, The file
src/app/shared-diary/upload/page.tsx is an empty placeholder and will render a
blank page; either implement the page component (export default async function
SharedDiaryUploadPage() { ... }) with the form and handlers, or replace the file
contents with a simple placeholder UI (e.g., a "Coming Soon / Under
Construction" React component) or disable the route by removing its export; pick
one approach, add basic accessibility and skeleton markup, and open an issue
referencing "shared-diary upload page implementation" if you want to defer full
work.

className="absolute top-1/2 left-4 -translate-y-1/2 cursor-pointer"
>
<BackIcon className="h-6 w-6" />
<BackIcon className={`text-${titleColor} h-6 w-6`} />
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

동적 Tailwind 클래스명이 작동하지 않습니다.

Tailwind CSS의 JIT 컴파일러는 빌드 타임에 정적 분석을 통해 클래스를 생성합니다. text-${titleColor} 형태의 동적 클래스명은 인식되지 않아 스타일이 적용되지 않습니다.

🔎 해결 방법

방법 1 (권장): 조건부 렌더링 사용

-        <BackIcon className={`text-${titleColor} h-6 w-6`} />
+        <BackIcon className={`${titleColor === "white" ? "text-white" : "text-black"} h-6 w-6`} />
       <h1
-        className={`text-h1 flex w-full items-center justify-center whitespace-nowrap text-${titleColor}`}
+        className={`text-h1 flex w-full items-center justify-center whitespace-nowrap ${titleColor === "white" ? "text-white" : "text-black"}`}
       >

방법 2: safelist에 추가 (tailwind.config에서)

// tailwind.config.js
module.exports = {
  safelist: [
    'text-white',
    'text-black',
  ],
  // ...
}

방법 3: style prop 사용

-        <BackIcon className={`text-${titleColor} h-6 w-6`} />
+        <BackIcon className="h-6 w-6" style={{ color: titleColor }} />

방법 1이 타입 안전성과 Tailwind의 디자인 시스템 활용 측면에서 가장 권장됩니다.

Also applies to: 33-33

🤖 Prompt for AI Agents
In @src/components/common/BackHeader.tsx at line 30, BackHeader uses a dynamic
Tailwind class `text-${titleColor}` which JIT won't generate; replace the
dynamic string with a deterministic class mapping or conditional rendering:
create a small map or switch keyed by the titleColor prop (e.g., map titleColor
values to 'text-white', 'text-black', etc.) and apply the resulting class to
BackIcon and the other affected element instead of `text-${titleColor}`, or
alternatively render conditional classNames for known values to ensure Tailwind
generates the CSS.

오늘의 하늘을 공유해주세요.
</div>
<button
className={`${status == "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

엄격한 동등 연산자를 사용하세요.

== 대신 ===를 사용하여 타입 안전성을 보장하세요.

🔎 수정 제안
-        className={`${status == "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`}
+        className={`${status === "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`}
📝 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
className={`${status == "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`}
className={`${status === "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"} text-button-sb h-[50px] w-[90px] cursor-pointer rounded-2xl text-white`}
🤖 Prompt for AI Agents
In @src/components/dailyPhoto/DailyMissionCard.tsx at line 35, In
DailyMissionCard update the conditional className expression to use strict
equality (replace `==` with `===`) when comparing the `status` variable so the
ternary `status === "NOT_STARTED" ? "bg-gradient-orange" : "bg-green-01"` is
used, ensuring type-safe comparison in the JSX expression that builds the
className string.

Comment on lines +26 to +27
const { id } = useParams<{ id: string }>();
const numericId = Number(id);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

ID 파라미터 검증을 추가하세요.

Number(id)NaN을 반환할 경우 라우팅 로직(82번 줄)에서 문제가 발생할 수 있습니다. ID가 유효한 숫자인지 검증하는 로직을 추가하는 것이 좋습니다.

🔎 제안하는 수정
  const { id } = useParams<{ id: string }>();
- const numericId = Number(id);
+ const numericId = Number(id);
+ 
+ if (isNaN(numericId)) {
+   // Handle invalid id - redirect or show error
+   router.push('/shared-diary');
+   return null;
+ }
📝 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
const { id } = useParams<{ id: string }>();
const numericId = Number(id);
const { id } = useParams<{ id: string }>();
const numericId = Number(id);
if (isNaN(numericId)) {
// Handle invalid id - redirect or show error
router.push('/shared-diary');
return null;
}
🤖 Prompt for AI Agents
In @src/components/dailyRecord/SharedDiaryChat.tsx around lines 26 - 27,
Validate the route id from useParams before using Number(id): ensure id exists
and parses to a finite integer (e.g., parseInt/Number +
Number.isFinite/Number.isInteger and > 0) and only assign numericId when valid;
if invalid, short-circuit the component (return null or an error UI) or perform
a safe redirect (using your routing navigate) instead of proceeding to the
routing logic that expects a number — update the numericId assignment and add
the guard around any code paths that use numericId (references: useParams, id,
numericId).

Comment on lines +1 to +8
export const formatDate = (createdAt: string): string => {
const date = new Date(createdAt);
const year = date.getFullYear().toString().slice(2);
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");

return `${year}${month}${day}일`;
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

잘못된 날짜 입력에 대한 처리를 추가하세요.

createdAt이 유효하지 않은 날짜 문자열인 경우 new Date(createdAt)는 Invalid Date를 반환하고, 최종 결과는 "aN년 aN월aN일" 형태가 됩니다. 날짜 유효성 검사를 추가하거나 에러 처리를 구현하는 것을 권장합니다.

🔎 에러 처리 추가 예시
 export const formatDate = (createdAt: string): string => {
   const date = new Date(createdAt);
+  
+  if (isNaN(date.getTime())) {
+    throw new Error("Invalid date string");
+  }
+  
   const year = date.getFullYear().toString().slice(2);
   const month = String(date.getMonth() + 1).padStart(2, "0");
   const day = String(date.getDate()).padStart(2, "0");

   return `${year}년 ${month}월${day}일`;
 };
🤖 Prompt for AI Agents
In @src/utils/formatDate.ts around lines 1 - 8, formatDate currently creates a
Date from createdAt without validation, producing "aN년 aN월aN일" for invalid
inputs; update the formatDate function to validate the Date (e.g., const date =
new Date(createdAt); if (isNaN(date.getTime())) { ... }) and handle invalid
cases by either throwing a clear error, returning a safe fallback string (like
"Invalid date" or ""), or accepting an optional default value; ensure all
downstream callers can handle the chosen fallback and keep the returned type
string.

typeof MISSION_STATUS[status].subtitle === "function"
? `${MISSION_STATUS[status].subtitle(currentDay)}요일`
: MISSION_STATUS[status].subtitle;

Copy link
Contributor

Choose a reason for hiding this comment

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

subtitle 계산 시 요일을 붙이는 부분이 "요일"을 하드코딩하고 있어서, 나중에 subtitle 함수가 이미 요일까지 포함한 문구를 반환한다면 중복될 수 있을 것 같아요.
subtitle(currentDay) 쪽에서 요일까지 포함해서 완성된 문장을 만들어 주도록 위임하거나, 최소한 subtitle이 문자열인지 함수인지 구분하는 로직을 유틸로 분리해 두면 좋을 것 같아요

Copy link
Member Author

Choose a reason for hiding this comment

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

유틸 분리하라는게 무슨말인지 이해가 안가요ㅠ 현재 로직은 subtitle을 미션 완료 상태에 따라서 상수로 빼둔 상태이고 미션이 NOT_STARTED 상태라면 요일의 앞부분을 함수로 가져오는 방식입니다.

현재의 요일의 인덱스를 가져와서 WEEK_DAYS에서 해당 요일을 한국어로 선언되어있는 배열에서 값을 가져오는 방식입니다.

Copy link
Contributor

Choose a reason for hiding this comment

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

const subtitle =
  typeof MISSION_STATUS[status].subtitle === "function"
    ? `${MISSION_STATUS[status].subtitle(currentDay)}요일`
    : MISSION_STATUS[status].subtitle;

여기서

  • subtitle이 함수인지 문자열인지 구분하고
  • 함수면 currentDay 넣어서 문장 만들고
  • 뒤에 "요일"까지 붙이는 일
    을 한 번에 하고 있어서, 이 부분을 유틸로 분리하는게 어떠냐는 제안이었습니다.
import { WEEK_DAYS } from "@/constants/dayToKorean";
import { MISSION_STATUS } from "@/constants/missionCard";

export const getMissionSubtitle = (
  status: keyof typeof MISSION_STATUS,
  todayIndex: number,
) => {
  const currentDay = WEEK_DAYS[todayIndex];
  const rawSubtitle = MISSION_STATUS[status].subtitle;

  const text =
    typeof rawSubtitle === "function"
      ? rawSubtitle(currentDay) 
      : rawSubtitle;

  return text;
};

이런식으로

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants