Conversation
- 피드백 생성 요청 화면 구현 - 그 외 공통 컴포넌트 네이밍 변경으로 인한 수정 - 네이밍 수정 작업 중 확인 된 오류 수정
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the 워크스루이번 풀 리퀘스트는 새로운 피드백 요청 기능을 추가하면서 프론트엔드 컴포넌트와 라우팅 구조를 개선합니다. 주요 변경사항으로는 marked 라이브러리 추가, 조수(Rouby) 어시스턴트의 일일 피드백 요청 뷰 및 메시지 렌더링 컴포넌트 신규 작성, 기존 UserSettingInput을 UserInput으로 통합하는 컴포넌트 교체, 어시스턴트 라우팅 구조 재편성, 그리고 입력 컴포넌트의 props 확대(autocomplete, maxlength 등)가 포함됩니다. 푸터 네비게이션이 업데이트되었고, 관련 API 엔드포인트와 유틸리티 함수들이 추가 또는 수정되었습니다. 시퀀스 다이어그램sequenceDiagram
actor User
participant DFR as DailyFeedbackRequestView
participant UFF as useFeedbackForm
participant API as Assistant API
participant Modal as BaseModal
User->>DFR: 기분 선택
DFR->>UFF: selectMood(moodKey)
UFF->>UFF: selectedMoodKey 업데이트
User->>DFR: 피드백 입력 및 제출
DFR->>DFR: onSubmit() 실행
DFR->>UFF: sendFeedbackRequest() 호출
rect rgb(200, 220, 255)
Note over UFF,API: 피드백 전송
UFF->>UFF: messages에 사용자 메시지 추가
UFF->>API: requestFeedback(userInput, mood)
API-->>UFF: 성공 응답
UFF->>UFF: messages에 완료 메시지 추가
UFF->>UFF: resultModal 상태 업데이트
end
UFF->>DFR: messages, resultModal 상태 반영
DFR->>Modal: 결과 모달 표시
User->>Modal: 확인 클릭
DFR->>DFR: handleModalConfirm() - 뒤로가기
예상 코드 리뷰 노력🎯 4 (Complex) | ⏱️ ~45-60분 판단 근거:
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
front/src/features/user/components/MyPageUserInfoForm.vue (2)
114-130: 시간 입력 필드의 접근성 개선 필요.시간 입력 필드들이
<span>요소와 함께 사용되고 있어 스크린 리더 사용자와 키보드 네비게이션에 문제가 있을 수 있습니다.<label>요소와for속성을 사용하여 접근성을 개선하세요.다음과 같이 수정을 권장합니다:
<div class="flex items-center justify-between"> - <span class="text-sm text-content-color">하루 시작 시간</span> + <label for="dailyStartTime" class="text-sm text-content-color">하루 시작 시간</label> <input + id="dailyStartTime" type="time" v-model="dailyStartTime" class="input bg-white w-[25%] min-w-[140px] p-3 border-border-color border rounded-2xl text-main-color h-12 placeholder-placeholder-pink-color" /> </div> <div class="flex items-center justify-between"> - <span class="text-sm text-content-color">하루 마감 시간</span> + <label for="dailyEndTime" class="text-sm text-content-color">하루 마감 시간</label> <input + id="dailyEndTime" type="time" v-model="dailyEndTime" class="input bg-white w-[25%] min-w-[140px] p-3 border-border-color border rounded-2xl text-main-color h-12 placeholder-placeholder-pink-color" /> </div>
119-119: 긴 인라인 Tailwind 클래스 추출 권장.시간 입력 필드에 동일한 긴 Tailwind 클래스 문자열이 반복됩니다. 재사용성과 유지보수성을 위해 공통 클래스로 추출하거나 컴포넌트화를 고려하세요.
예시:
<script setup> const timeInputClass = "input bg-white w-[25%] min-w-[140px] p-3 border-border-color border rounded-2xl text-main-color h-12 placeholder-placeholder-pink-color" </script> <template> <input type="time" v-model="dailyStartTime" :class="timeInputClass" /> </template>Also applies to: 128-128
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (1)
front/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (30)
front/package.json(1 hunks)front/src/components/Footer.vue(3 hunks)front/src/components/Header.vue(1 hunks)front/src/components/common/BaseInput.vue(2 hunks)front/src/components/common/BaseModal.vue(2 hunks)front/src/components/common/LoadingSpinner.vue(1 hunks)front/src/components/common/UserInput.vue(3 hunks)front/src/features/assistant/api.js(1 hunks)front/src/features/assistant/assistantService.js(1 hunks)front/src/features/assistant/component/BriefingForm.vue(2 hunks)front/src/features/assistant/component/Messages.vue(1 hunks)front/src/features/assistant/constants.js(1 hunks)front/src/features/assistant/useBriefingForm.js(4 hunks)front/src/features/assistant/useFeedbackForm.js(1 hunks)front/src/features/assistant/views/DailyBriefingView.vue(1 hunks)front/src/features/assistant/views/DailyFeedbackRequestView.vue(1 hunks)front/src/features/onboard/components/HealthCheckForm.vue(2 hunks)front/src/features/onboard/components/ProfileSettingForm.vue(2 hunks)front/src/features/onboard/components/SpeechSettingForm.vue(2 hunks)front/src/features/routine-task/RoutineTaskService.js(0 hunks)front/src/features/schedule/scheduleService.js(2 hunks)front/src/features/user/api.js(1 hunks)front/src/features/user/components/MyPageResetPasswordForm.vue(5 hunks)front/src/features/user/components/MyPageRoubySettingForm.vue(4 hunks)front/src/features/user/components/MyPageUserInfoForm.vue(3 hunks)front/src/features/user/useMyPageForm.js(1 hunks)front/src/router/index.js(4 hunks)front/src/shared/firebase/config.js(1 hunks)front/src/shared/utils/dateTimeUtils.js(2 hunks)front/src/shared/utils/dateUtils.js(2 hunks)
💤 Files with no reviewable changes (1)
- front/src/features/routine-task/RoutineTaskService.js
🧰 Additional context used
🧬 Code graph analysis (7)
front/src/features/assistant/useBriefingForm.js (2)
front/src/router/index.js (1)
router(3-238)front/src/shared/utils/dateUtils.js (2)
parseYMD(86-87)parseYMD(86-87)
front/src/shared/utils/dateUtils.js (1)
front/src/shared/utils/rruleUtils.js (1)
d(40-40)
front/src/router/index.js (2)
front/src/features/auth/usePasswordForm.js (1)
route(18-18)front/src/shared/composable/useActivePath.js (1)
route(4-4)
front/src/features/assistant/useFeedbackForm.js (3)
front/src/features/assistant/assistantService.js (4)
res(12-12)res(17-17)requestFeedback(16-19)requestFeedback(16-19)front/src/features/assistant/api.js (2)
requestFeedback(7-9)requestFeedback(7-9)front/src/features/assistant/constants.js (2)
MAX_LEN(35-37)MAX_LEN(35-37)
front/src/features/assistant/api.js (1)
front/src/features/assistant/assistantService.js (2)
requestFeedback(16-19)requestFeedback(16-19)
front/src/features/schedule/scheduleService.js (2)
front/src/features/schedule/api.js (4)
deleteSchedule(26-32)deleteSchedule(26-32)deleteSchedulesStartingFrom(34-40)deleteSchedulesStartingFrom(34-40)front/src/shared/utils/errorUtils.js (1)
wrapApi(33-41)
front/src/features/assistant/assistantService.js (2)
front/src/features/assistant/api.js (4)
getBriefing(3-5)getBriefing(3-5)requestFeedback(7-9)requestFeedback(7-9)front/src/shared/utils/errorUtils.js (1)
wrapApi(33-41)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: test
🔇 Additional comments (33)
front/src/features/schedule/scheduleService.js (3)
2-8: LGTM! 가독성 향상을 위한 좋은 포맷팅입니다.멀티라인 import 구문과 trailing comma 사용은 다음과 같은 이점을 제공합니다:
- Git diff에서 변경사항 추적이 용이
- 향후 import 추가/삭제 시 더 깔끔한 변경 이력
35-40: LGTM! 코드 일관성을 개선하는 좋은 리팩토링입니다.
wrapApi의 멀티라인 포맷을 사용하여 파일 내 다른 함수들(createSchedule,getSchedules,updateSchedule)과 일관된 스타일을 유지하고 있습니다. 파라미터 구조분해도 명시적으로 작성되어 API 인터페이스가 명확합니다.
42-47: LGTM! 일관된 코드 스타일을 유지하는 리팩토링입니다.
deleteSchedule와 동일한 패턴으로 리팩토링되어 코드베이스 전반의 일관성이 향상되었습니다. 파라미터 구조분해를 통해 함수가 기대하는 입력값이 명확하게 표현되어 있습니다.front/src/shared/firebase/config.js (1)
90-99: 코드 구조가 명확합니다.빈 줄 추가로 함수 정의와 이벤트 리스너 등록을 명확히 구분하여 가독성이 개선되었습니다.
resolvePropagatedEvent핸들러를 BroadcastChannel과 Service Worker 양쪽에 등록하여 탭 간 이벤트 중복 제거를 올바르게 구현했습니다.front/src/features/user/useMyPageForm.js (1)
18-23: 리뷰 코멘트는 부정확합니다. 코드 변경사항은 문제없습니다.검증 결과, useMyPageForm.js 파일의 라우트 이름 변경은 모두 라우터 설정에 정의되어 있습니다:
'user-info': front/src/router/index.js 라인 28에 정의됨'password-reset': front/src/router/index.js 라인 22에 정의됨'rouby-setting': front/src/router/index.js 라인 34에 정의됨따라서 라우트 이름이 유효하며 런타임 내비게이션 오류가 발생하지 않습니다. 이 파일의 코드 변경사항은 라우터 설정과 일치하므로 승인 가능합니다.
Likely an incorrect or invalid review comment.
front/src/features/user/api.js (1)
15-17: 원본 리뷰 코멘트는 잘못된 것입니다.백엔드 API 정의를 확인한 결과, 엔드포인트 분리는 의도적인 설계입니다:
/v1/users/basic-info(GET): 읽기 전용 기본 사용자 정보/v1/users/user-info(PATCH): 수정 가능한 사용자 프로필 정보이는 백엔드 Java 테스트(
UserControllerTest.java)와 HTTP 테스트 파일(user.http)에서 명확히 확인되며, 프론트엔드 여러 곳에서 일관되게 사용되고 있습니다. 함수명myPageReadUserInfo와myPageUpdateUserInfo는 각각의 엔드포인트 목적을 정확하게 반영하고 있습니다.Likely an incorrect or invalid review comment.
front/src/shared/utils/dateTimeUtils.js (2)
19-19: LGTM!12시간 형식의 시간 패턴 추가가 적절합니다.
61-63: LGTM!코드 포맷팅 변경이며 로직은 동일합니다.
front/src/features/assistant/api.js (1)
7-9: LGTM!피드백 요청 API 함수가 적절하게 구현되었습니다. payload 검증은 service 레이어에서 처리되는 것으로 확인됩니다.
front/src/features/assistant/assistantService.js (1)
1-19: LGTM!service 레이어의 구조가 일관성 있게 리팩토링되었으며, 새로운
requestFeedback함수가 적절히 추가되었습니다.wrapApi를 통한 에러 처리도 올바르게 적용되었습니다.front/src/components/common/BaseInput.vue (1)
33-37: LGTM!label 엘리먼트의 포맷팅이 개선되었습니다.
front/src/features/user/components/MyPageResetPasswordForm.vue (2)
3-8: LGTM!패스워드 매니저 호환성을 위한 hidden username 필드 추가는 보안 모범 사례입니다.
15-15: LGTM!autocomplete 속성들이 HTML 표준에 맞게 올바르게 설정되었습니다:
current-password: 기존 비밀번호new-password: 새 비밀번호 및 확인Also applies to: 31-31, 50-50
front/src/components/common/BaseModal.vue (1)
22-22: LGTM!'close' 이벤트 추가로 부모 컴포넌트에서 추가 동작을 처리할 수 있게 되었습니다.
front/src/features/assistant/useBriefingForm.js (1)
13-13: 날짜 초기화 로직의 일관성을 개선하세요.
datePickStore.lastSelectedDate는 항상 유효한 'yyyy-MM-dd' 형식 문자열을 반환하므로 undefined 또는 Invalid Date 발생 우려는 없습니다. 다만 다음 불일치가 있습니다:
initDate존재 시:parseYMD()사용 (date-fns parse 함수)initDate미존재 시:new Date()직접 사용 (브라우저 기본 파싱)일관성을 위해 두 경로 모두
parseYMD()사용을 권장합니다:const selectedDate = ref(parseYMD(initDate ?? datePickStore.lastSelectedDate))front/src/features/assistant/constants.js (1)
1-42: 잘 구조화된 상수 정의입니다.기분 상태별 데이터 구조가 일관되게 정의되어 있고, 최대 길이 및 에러 메시지 상수도 명확합니다. 코드의 유지보수성과 가독성이 좋습니다.
front/src/features/assistant/views/DailyBriefingView.vue (1)
17-21: LGTM!
BriefingForm컴포넌트의 prop 이름 변경(content→htmlContent)에 맞춰 바인딩이 올바르게 업데이트되었습니다.front/src/features/onboard/components/HealthCheckForm.vue (1)
3-3: LGTM!
UserSettingInput에서UserInput으로 컴포넌트 이름이 일관되게 변경되었습니다. prop과 이벤트 바인딩이 그대로 유지되어 기능상 문제가 없습니다.Also applies to: 42-48
front/src/features/onboard/components/ProfileSettingForm.vue (1)
3-3: LGTM!
UserSettingInput에서UserInput으로 컴포넌트가 일관되게 변경되었습니다. 다른 온보딩 컴포넌트들과 동일한 리팩토링 패턴을 따르고 있습니다.Also applies to: 55-61
front/src/features/user/components/MyPageRoubySettingForm.vue (1)
3-3: LGTM!여러 개선사항이 잘 적용되었습니다:
UserSettingInput에서UserInput으로 일관된 컴포넌트 변경useGoBack훅을 사용한 취소 처리 로직 중앙화- 구조 분해 할당에 trailing comma 추가 (좋은 practice)
취소 버튼 처리가 emit에서 composable 함수 호출로 변경되어 코드가 더 명확해졌습니다.
Also applies to: 9-9, 21-22, 24-26, 47-52, 81-84
front/src/components/Header.vue (1)
9-9: 라우트 경로 변경이 올바르게 정의되어 있습니다.검증 결과, 다음을 확인했습니다:
- ✅
/routine-task경로가 라우터에 올바르게 정의됨 (front/src/router/index.js 124-150줄)- ✅ 자식 라우트들(daily/list, create, detail)이 모두 정상 설정됨
- ✅ Header.vue가 새로운
/routine-task경로를 올바르게 사용 중- ✅ Footer.vue도 동일하게
/routine-task로 이미 마이그레이션됨- ✅ 구
/routine경로에 대한 참조가 코드베이스에 없음 (완전한 리팩토링)라우트 변경이 완벽하게 구현되어 있어 추가 조치가 필요 없습니다.
front/src/features/assistant/component/BriefingForm.vue (1)
2-2: 미사용 변수 제거 필요
content변수가 destructuring되었으나 사용되지 않습니다.다음 diff를 적용하여 수정하세요:
-const { loading, error, content } = defineProps({ +const { loading, error, htmlContent } = defineProps({Likely an incorrect or invalid review comment.
front/src/features/onboard/components/SpeechSettingForm.vue (1)
19-19: 컴포넌트 이름 변경이 올바르게 적용되었습니다.UserSettingInput에서 UserInput으로의 리네이밍이 일관되게 적용되어 있으며, props 및 이벤트 처리도 동일하게 유지되고 있습니다.
Also applies to: 33-33
front/src/features/assistant/useFeedbackForm.js (2)
39-43: 입력 길이 제한 로직이 잘 구현되었습니다.watch를 사용하여 MAX_LEN을 초과하는 입력을 자동으로 잘라내는 방식이 적절합니다.
6-6: 검증 완료: 문제 없음
selectedMoodKey초기값으로 사용된 'soso'는 constants.js의 MOODS 객체에 정확하게 정의되어 있습니다. 모든 필수 속성(key, val, day, placeholder)이 포함되어 있으므로 코드는 올바릅니다.front/src/features/assistant/views/DailyFeedbackRequestView.vue (2)
31-33: textarea에서 Shift+Enter 동작을 확인하세요.Line 32에서
shiftKey체크로 Shift+Enter를 차단하고 있습니다. 일반적으로 textarea에서는 Shift+Enter로 줄바꿈을 입력할 수 있어야 하는데, 현재 구현은 Enter만으로 줄바꿈이 가능한지 확인이 필요합니다.UserInput 컴포넌트가 textarea를 사용하므로, 사용자가 줄바꿈을 입력할 수 있는 방법이 있는지 확인하세요. 만약 줄바꿈이 필요 없다면 현재 로직이 적절하지만, 줄바꿈이 필요하다면 다음과 같이 수정을 고려하세요:
const onSubmit = (e) => { - if (e.shiftKey) return + // Shift+Enter는 줄바꿈, Enter만 전송 + if (!e.shiftKey) { + e.preventDefault() if (userInput.value.trim() === '') return // ... rest of submit logic + } }
20-29: Line 25: UserInput ref에서 불필요한$el접근을 제거하세요.UserInput 컴포넌트는 일반
<textarea>요소만 렌더링하며defineExpose를 사용하지 않습니다. Vue 3에서 template ref는 자동으로 컴포넌트 루트 요소(DOM 요소)를 노출하므로,userInputRef.value는 이미<textarea>요소입니다. 따라서userInputRef.value.$el이 아닌userInputRef.value로 직접 접근해야 합니다.수정:
userInputRef.value.scrollIntoView({ behavior: 'smooth', block: 'end', })Likely an incorrect or invalid review comment.
front/src/components/common/UserInput.vue (2)
84-90: 버튼 위치 계산을 검토하세요.Line 87에서
bottom-1.5 -translate-y-1/2를 사용하고 있는데,bottom포지셔닝과-translate-y-1/2를 함께 사용하는 것이 의도한 위치를 만드는지 확인이 필요합니다. 일반적으로translate-y-1/2는top-1/2와 함께 사용됩니다.의도한 버튼 위치가 textarea의 하단 근처라면 다음과 같이 수정을 고려하세요:
- class="absolute right-3 bottom-1.5 -translate-y-1/2 w-5 h-5 p-0" + class="absolute right-3 bottom-3 w-5 h-5 p-0"또는 textarea 중앙에 위치시키려면:
- class="absolute right-3 bottom-1.5 -translate-y-1/2 w-5 h-5 p-0" + class="absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 p-0"
59-83: review comment에 부정확한 정보가 포함되어 있습니다.검증 결과:
error prop 타입 변경 - 거짓: UserInput.vue의 error prop은 여전히
type: String, default: ''입니다. 객체 타입이 아닙니다.submit 이벤트 변경 - 실제:
emit('submit', $event)로 이벤트 객체를 전달합니다. 다만 실제 사용처(addHealthStatusKeywordsTag,addProfileKeywordsTag,addToneTag)는 모두 인자를 받지 않으므로 현재는 호환됩니다.input → textarea 변경 - 실제: textarea로 변경되었으나, Vue에서
v-model과@input이벤트는 동일하게 작동합니다.실제 영향 분석:
- 7개 파일에서 UserInput을 사용 중
- 모든 핸들러가 emit 인자를 무시하므로 즉각적인 breaking change 없음
- error prop은 여전히 String 타입이므로 사용처 호환성 유지
Review comment는 검증되지 않은 주장(error prop 타입 변경)을 포함하고 있으며, 실제 breaking change 위험은 제시된 것보다 미미합니다.
Likely an incorrect or invalid review comment.
front/src/router/index.js (1)
85-122: 새로운 assistant 라우팅 구조가 잘 설계되었습니다.assistant 기능을 위한 중첩 라우트 구조가 명확하고 논리적으로 구성되어 있습니다. date 파라미터의 정규식 패턴도 적절합니다.
front/src/components/Footer.vue (1)
76-84: 더보기 버튼 UX 패턴이 잘 구현되었습니다.토글 방식의 floating 메뉴 구현이 직관적이며, create/modify 페이지에서는 표시하지 않는 조건부 렌더링도 적절합니다.
front/src/features/user/components/MyPageUserInfoForm.vue (2)
1-35: 스크립트 구성이 잘 되어 있습니다.Composition API와 새로운
useGoBack통합이 올바르게 구현되었으며, 초기 설정 로딩과 취소 핸들러가 적절합니다.
69-76: <!-- [scratchpad_start]
[task verification_2/2 complete]
모든 필요한 정보 수집 및 분석 완료[observations]
UserInput 컴포넌트:
- error prop을 받아서 border 색상 변경 (error-color vs border-color)
- focus 시 box-shadow 추가 (error && isFocused)
- 에러 상태를 입력 필드의 시각적 표시로 처리
FieldError 컴포넌트:
- message prop을 받아서 text-error-color로 텍스트 표시
- v-if="message.trim().length"로 메시지 있을 때만 렌더링
- 에러 메시지를 텍스트로 표시
MyPageUserInfoForm 사용:
- 동일한 에러 변수를 UserInput과 FieldError 모두에 전달
- 두 위치 모두 동일 패턴
[analysis]
- UserInput: 입력 필드 자체의 "상태" 시각화 (border/shadow)
- FieldError: 에러 "메시지" 텍스트 표시
- 중복이 아니라 보완적 역할 (UI 패턴: 상태 표시 + 메시지 표시)
- 리뷰 코멘트의 우려사항은 부정확함
[done]
리뷰 코멘트는 로 분류됨
</scratchpad_end] -->리뷰 코멘트는 부정확합니다. 에러 표시가 중복되지 않습니다.
UserInput은errorprop을 받아 입력 필드의 상태를 시각적으로 표시합니다(테두리 색상:border-error-color, 포커스 시 그림자 효과). 반면FieldError는 에러 메시지를 텍스트로 표시합니다.이는 중복이 아니라 일반적인 UI 패턴으로, 사용자에게 다음 정보를 제공합니다:
- UserInput: 이 필드에 문제가 있습니다 (시각적 표시)
- FieldError: 구체적으로 무엇이 문제인지 설명 (메시지 표시)
두 컴포넌트는 서로 다른 목적으로 설계되었으며, 함께 사용하는 것이 올바른 구현입니다.
Likely an incorrect or invalid review comment.
| "lodash-es": "^4.17.21", | ||
| "lucide-vue-next": "^0.525.0", | ||
| "luxon": "^3.7.1", | ||
| "marked": "^16.4.1", |
There was a problem hiding this comment.
🧩 Analysis chain
marked 라이브러리 사용 시 XSS 보안 주의 필요
marked 라이브러리는 마크다운을 HTML로 변환하지만, 출력을 자동으로 sanitize하지 않습니다. v-html과 함께 사용할 경우 XSS 공격에 취약할 수 있습니다.
Based on learnings
다음 스크립트를 실행하여 marked 사용처를 확인하고 sanitization 처리 여부를 검증해주세요:
DOMPurify와 같은 sanitization 라이브러리를 추가하여 marked 출력을 정제한 후 렌더링하는 것을 권장합니다.
🏁 Script executed:
#!/bin/bash
# marked 라이브러리를 import하는 모든 파일 찾기
echo "=== marked를 import하는 파일들 ==="
rg -n "import.*marked" --type=js --type=vue -C 3
echo -e "\n=== v-html 사용처 확인 ==="
rg -n "v-html" --type=vue -C 3
echo -e "\n=== DOMPurify 같은 sanitization 라이브러리 사용 여부 ==="
rg -n "DOMPurify|sanitize" --type=js --type=vue -C 2Length of output: 182
🏁 Script executed:
#!/bin/bash
# Search for marked imports - try without vue type
echo "=== marked를 import하는 파일들 ==="
rg -n "import.*marked" --type=js --type=ts -C 3
# Also search in .vue files directly
echo -e "\n=== .vue 파일에서 marked import 찾기 ==="
rg -n "import.*marked" -g "*.vue" -C 3
# Search for v-html usage in vue files
echo -e "\n=== v-html 사용처 ==="
rg -n "v-html" -g "*.vue" -C 3
# Check for marked usage in vue files without import filtering
echo -e "\n=== .vue 파일에서 marked 사용 패턴 ==="
rg -n "marked" -g "*.vue" -C 2
# Check for sanitization libraries
echo -e "\n=== DOMPurify 및 sanitize 사용 여부 ==="
rg -n "DOMPurify|sanitize" --type=js --type=ts -C 2
rg -n "DOMPurify|sanitize" -g "*.vue" -C 2
# Check package.json for sanitization libraries
echo -e "\n=== package.json에서 보안 관련 라이브러리 ==="
cat front/package.json | grep -i "purify\|sanitize\|xss" || echo "No sanitization library found in package.json"Length of output: 2645
🏁 Script executed:
# Since we can't clone without knowing repo details, let's use cat if available
cat front/package.json 2>/dev/null | head -50Length of output: 1386
🏁 Script executed:
#!/bin/bash
# Search for marked imports - try without vue type
echo "=== marked를 import하는 파일들 ==="
rg -n "import.*marked" --type=js --type=ts -C 3
# Also search in .vue files directly
echo -e "\n=== .vue 파일에서 marked import 찾기 ==="
rg -n "import.*marked" -g "*.vue" -C 3
# Search for v-html usage in vue files
echo -e "\n=== v-html 사용처 ==="
rg -n "v-html" -g "*.vue" -C 3
# Check for marked usage in vue files without import filtering
echo -e "\n=== .vue 파일에서 marked 사용 패턴 ==="
rg -n "marked" -g "*.vue" -C 2
# Check for sanitization libraries
echo -e "\n=== DOMPurify 및 sanitize 사용 여부 ==="
rg -n "DOMPurify|sanitize" --type=js --type=ts -C 2
rg -n "DOMPurify|sanitize" -g "*.vue" -C 2
# Check package.json for sanitization libraries
echo -e "\n=== package.json에서 보안 관련 라이브러리 ==="
cat front/package.json | grep -i "purify\|sanitize\|xss" || echo "No sanitization library found in package.json"Length of output: 2645
🏁 Script executed:
#!/bin/bash
# Check how marked is being used in useBriefingForm.js
echo "=== useBriefingForm.js - marked 사용 방식 ==="
cat front/src/features/assistant/useBriefingForm.js
echo -e "\n=== BriefingForm.vue - htmlContent 출처 ==="
cat front/src/features/assistant/component/BriefingForm.vue
echo -e "\n=== Messages.vue - msg.txt 출처 ==="
cat front/src/features/assistant/component/Messages.vueLength of output: 4906
🏁 Script executed:
#!/bin/bash
# Check assistantService to see where msg.txt and content come from
echo "=== assistantService.js 확인 ==="
find front -name "assistantService*" -type f -exec cat {} \;
echo -e "\n=== Messages와 htmlContent를 사용하는 상위 컴포넌트 ==="
rg -n "BriefingForm|Messages" -g "*.vue" -A 5 -B 5 | head -80Length of output: 4805
marked 라이브러리의 XSS 보안 취약점 - 즉시 수정 필요
marked 라이브러리는 마크다운을 HTML로 변환하지만 자동으로 sanitize하지 않으며, 현재 코드에서 이 출력을 v-html로 렌더링하고 있어 XSS 공격에 노출되어 있습니다.
확인된 취약점:
front/src/features/assistant/useBriefingForm.js줄 40:form.htmlContent = marked.parse(data.content)- sanitization 없이 마크다운 파싱front/src/features/assistant/component/BriefingForm.vue줄 19:<div v-html="htmlContent"></div>- HTML 직접 렌더링front/src/features/assistant/component/Messages.vue줄 64:<div v-html="msg.txt"></div>- HTML 직접 렌더링
필수 조치:
- DOMPurify 또는 isomorphic-dompurify를
package.json에 추가 - marked 출력을 sanitize 처리 후 렌더링:
form.htmlContent = DOMPurify.sanitize(marked.parse(data.content))
🤖 Prompt for AI Agents
In front/package.json around line 31, the project lacks a sanitization library
and currently uses marked without sanitizing HTML output; add a safe DOM
sanitizer dependency (e.g., "dompurify" or "isomorphic-dompurify") to
dependencies with a specific version (pick a recent stable semver), save the
change to package.json, and run install so the code can be updated to wrap
marked.parse(...) results with the sanitizer before any v-html rendering.
| <BaseInput | ||
| v-model="nickname" | ||
| type="text" | ||
| placeholder="이름" | ||
| class="mt-2" | ||
| /> |
There was a problem hiding this comment.
🧩 Analysis chain
입력 컴포넌트 일관성 및 검증 누락.
닉네임 필드는 BaseInput을 사용하는 반면, 건강 상태와 관심 태그는 UserInput을 사용합니다. 일관성을 위해 동일한 컴포넌트를 사용하는 것을 고려하세요. 또한 닉네임 필드에는 에러 처리나 검증이 없어 보입니다.
다음 스크립트로 UserInput과 BaseInput의 차이점 및 검증 로직을 확인할 수 있습니다:
🏁 Script executed:
#!/bin/bash
# UserInput과 BaseInput의 구현 차이 확인
echo "=== UserInput 구현 ==="
fd -t f "UserInput.vue" --exec cat {}
echo -e "\n=== BaseInput 구현 ==="
fd -t f "BaseInput.vue" --exec cat {}
echo -e "\n=== useUserInfoForm에서 nickname 검증 여부 확인 ==="
fd -t f "useUserInfoForm.js" --exec cat {}Length of output: 7264
검증을 위해 MyPageUserInfoForm.vue 파일의 전체 템플릿을 확인해야 합니다.웹 검색은 일반적인 Vue 3 검증 패턴을 보여주지만, 실제 파일의 구현을 확인할 필요가 있습니다. 코드베이스에서 템플릿 파일을 찾겠습니다.
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# MyPageUserInfoForm.vue 파일 찾아서 전체 내용 확인
fd -t f "MyPageUserInfoForm.vue" --exec cat {}Length of output: 4089
닉네임 필드에 검증 로직과 에러 상태 추가 필요.
현재 닉네임 필드는 BaseInput으로 구현되어 있지만 useUserInfoForm 컴포저블에 닉네임 검증 로직과 에러 상태(ref)가 없습니다. 건강 상태와 관심 태그는 UserInput + healthStatusKeywordError / profileKeywordError 와 같이 검증이 구현되어 있습니다. 닉네임도 동일하게 에러 상태를 추가하고 검증 로직을 구현해야 합니다.
추가로:
- 컴포넌트 일관성: 태그 입력 필드들이
UserInput(textarea)을 사용하는데, 닉네임은BaseInput(input)을 사용하고 있습니다. 의도적 차이가 아니라면 통일을 검토하세요. - 접근성: 시간 입력 필드(114-130 라인)에서
<span>을 라벨로 사용하고 있습니다. 스크린 리더 지원을 위해<label>요소 또는for속성과id연결이 필요합니다.
🤖 Prompt for AI Agents
front/src/features/user/components/MyPageUserInfoForm.vue lines 42-47: the
nickname field currently uses BaseInput without any validation or error state;
update the useUserInfoForm composable to add a nickname ref and a nicknameError
ref, implement the same validation rules used for healthStatus/profile keywords
(length, forbidden chars, uniqueness as applicable) and expose a
validateNickname method; bind nickname and nicknameError from the composable to
the input so the component shows validation feedback; also consider switching
the nickname BaseInput to UserInput for consistency with other tag inputs if
there is no intentional difference; additionally,
front/src/features/user/components/MyPageUserInfoForm.vue lines 114-130: replace
the span labels for the time inputs with proper <label> elements and associate
them to the inputs via id/for attributes to improve accessibility.
- 텍스트 내용 새니타이징 추가 외 - 불필요한 디렉토리 제거하여 병합
- 새니타이징 UI로 책임 이동
- 누락한 임포트 문 추가 - 패키지 json에 엔진 버전 명시
#️⃣연관된 이슈
변경 타입
변경 내용
as-is
to-be(변경 후 설명을 여기에 작성)
체크리스트
코멘트
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선 사항
버그 수정