Skip to content

Conversation

@sunhwaaRj
Copy link
Collaborator

@sunhwaaRj sunhwaaRj commented Nov 7, 2025

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 다중 선택 버튼 그리드 UI 추가
  • 스타일

    • 기본 버튼 배경색 업데이트
    • 진행률 표시줄 색상 변경
    • 온보딩 단계 확대 (6단계로 확장)
  • 기타

    • 한국어 비즈니스 카테고리 지원 추가
    • 파일 업로드 완료 메시지 개선

@coderabbitai
Copy link

coderabbitai bot commented Nov 7, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

온보딩 UI와 파일 업로드 경로/요청 방식을 변경합니다. 버튼 색상 및 진행바 색상을 업데이트하고 MultiSelect에 선택 상태와 버튼 그리드를 도입했으며, SingleSelect 기본 옵션과 채팅 업로드 메시지를 수정하고 파일 업로드를 axios + 환경변수 URL로 전환했습니다.

Changes

응집도 / 파일(들) 변경 사항 요약
버튼·진행바 스타일
src/components/onboarding/FormButton.tsx, src/pages/onboarding/Onboarding.tsx
기본 액션 버튼 배경색을 #1d4ed8에서 #0D2D84로 변경; Onboarding의 프로그레스바 색상도 #0D2D84로 변경, 총 단계 수 5→6
MultiSelect 컴포넌트 추가/개선
src/components/onboarding/MultiSelect.tsx
MultiSelectProps 인터페이스 추가, 내부 selectedOptions 상태 및 toggleOption 핸들러 도입, 텍스트 입력에서 버튼 그리드 UI로 전환, 기본 옵션 목록 제공 및 default export
SingleSelect 옵션 변경
src/components/onboarding/SingleSelect.tsx
defaultOptions 레이블을 영어에서 한국어 비즈니스 카테고리명(1~7번 id 유지)으로 변경
채팅 업로드 메시지 변경
src/pages/chat/ChatPageTest.tsx
파일 업로드 성공 메시지 내용을 더 상세한 문구(여러 줄)로 변경
파일 업로드 네트워크 처리 변경
src/components/chat/FileSendButton.tsx, src/api/api.ts
apiClient 기반 동적 baseURL 해석 제거(고정 baseURL 사용으로 변경됨); FileSendButton은 더 이상 내부 apiClient를 사용하지 않고 axios.post(uploadUrl, formData, ...)로 업로드하며 VITE_UPLOAD_URL에서 업로드 대상 URL을 읽음, 업로드 URL 콘솔 로깅 추가

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant U as User (UI)
    participant C as FileSendButton
    participant S as UploadEndpoint (env VITE_UPLOAD_URL)

    rect rgb(230,245,255)
    note right of C: 새 흐름 (axios + env URL)
    end

    U->>C: 파일 선택 + 전송
    C->>S: POST formData (axios.post(uploadUrl,...))
    S-->>C: 200 OK (업로드 성공)
    C-->>U: 업로드 성공 처리 / 메시지 표시
Loading

(참고: 이전 흐름은 내부 apiClient.post('/chat/upload', ...) 호출이었음 — 이제 외부 URL을 직접 사용)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • 주의할 파일/영역:
    • MultiSelect.tsx: 선택 토글 로직, 초기 옵션/props 동작, 렌더링 키 및 액티브 스타일 확인
    • FileSendButton.tsx: 업로드 URL(env) 해석, axios 요청 헤더(멀티파트) 및 에러/성공 처리 흐름 확인
    • api.ts: baseURL을 고정값으로 바꿈에 따른 환경별 배포 영향 검토
    • 스타일 변경: FormButtonOnboarding 사이 색상 값 일관성 확인

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive PR 제목이 온보딩 페이지의 여러 컴포넌트 변경사항(버튼 색상, 다중선택, 단일선택, 진행률 표시줄, 단계 증가 등)을 포괄적으로 반영하고 있으나, '마무리'라는 표현이 구체적이지 않아 실제 변경 내용을 명확히 전달하지 못함. PR 제목을 더 구체적으로 수정하여 주요 변경사항을 명확히 나타내도록 권장합니다. 예: '온보딩 페이지 UI 업데이트 및 단계 확장' 또는 유사한 표현으로 변경을 고려하세요.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a5ae8e and abb23ab.

📒 Files selected for processing (2)
  • src/api/api.ts (1 hunks)
  • src/components/chat/FileSendButton.tsx (1 hunks)

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

@github-actions
Copy link

github-actions bot commented Nov 7, 2025

@github-actions
Copy link

github-actions bot commented Nov 7, 2025

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: 2

Caution

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

⚠️ Outside diff range comments (2)
src/components/onboarding/SingleSelect.tsx (1)

11-29: 선택된 값을 수집할 수 없는 구조입니다.

현재 select 요소가 valueonChange 속성 없이 비제어 컴포넌트로 구현되어 있어, 사용자가 선택한 값을 부모 컴포넌트(Onboarding)에서 가져올 수 없습니다. 온보딩 플로우가 완료되어도 실제 폼 데이터를 수집하거나 제출할 수 없게 됩니다.

다음과 같이 제어 컴포넌트 패턴으로 수정하세요:

-const SingleSelect = ({ title }: { title: string }) => {
+interface SingleSelectProps {
+  title: string;
+  value: string;
+  onChange: (value: string) => void;
+}
+
+const SingleSelect = ({ title, value, onChange }: SingleSelectProps) => {
   return (
     <div className="flex flex-col gap-6">
       {/* 제목 */}
       <p className="text-[24px] font-semibold">{title} 선택하세요</p>
       {/* 단일 선택 버튼 */}
-      <select className="rounded-xl border border-gray-200 px-2 py-4 text-[20px] outline-none focus:border-gray-200">
+      <select 
+        value={value}
+        onChange={(e) => onChange(e.target.value)}
+        className="rounded-xl border border-gray-200 px-2 py-4 text-[20px] outline-none focus:border-gray-200"
+      >
         <option className="" value="">
           선택하세요
         </option>
src/pages/onboarding/Onboarding.tsx (1)

10-46: 온보딩 폼 데이터를 수집하고 관리하는 상태가 없습니다.

현재 구조에서는 사용자가 각 단계에서 입력한 값들(업종, 업체명, 위치, 매출, 타겟층)을 저장하거나 수집할 방법이 없습니다. handleComplete 함수는 단순히 페이지만 이동하며, 실제 온보딩 데이터는 모두 손실됩니다. 이는 온보딩 기능을 완전히 무용지물로 만드는 치명적인 문제입니다.

다음과 같이 폼 상태 관리를 추가하세요:

 const Onboarding = () => {
   const navigate = useNavigate();
   const [currentStep, setCurrentStep] = useState(1);
   const totalSteps = 6;
+  
+  // 폼 데이터 상태
+  const [formData, setFormData] = useState({
+    businessType: '',
+    businessName: '',
+    location: '',
+    averageRevenue: '',
+    targetRevenue: '',
+    targetAudience: [] as string[],
+  });

   const handleComplete = () => {
+    // 폼 데이터 유효성 검사
+    console.log('Submitting onboarding data:', formData);
+    // TODO: API 호출로 데이터 전송
     navigate('/chat');
   };

   const renderStepContent = () => {
     switch (currentStep) {
       case 1:
-        return <SingleSelect key="step1" title="업종을" />;
+        return (
+          <SingleSelect 
+            key="step1" 
+            title="업종을" 
+            value={formData.businessType}
+            onChange={(value) => setFormData(prev => ({ ...prev, businessType: value }))}
+          />
+        );
       case 2:
-        return <TextInput key="step2" title="업체명을" placeholder="" />;
+        return (
+          <TextInput 
+            key="step2" 
+            title="업체명을" 
+            placeholder=""
+            value={formData.businessName}
+            onChange={(value) => setFormData(prev => ({ ...prev, businessName: value }))}
+          />
+        );
       // ... 나머지 케이스도 동일하게 수정

이후 TextInputPriceInput 컴포넌트도 SingleSelect, MultiSelect와 같이 제어 컴포넌트 패턴으로 수정해야 합니다.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7695a7a and 2a5ae8e.

📒 Files selected for processing (5)
  • src/components/onboarding/FormButton.tsx (1 hunks)
  • src/components/onboarding/MultiSelect.tsx (1 hunks)
  • src/components/onboarding/SingleSelect.tsx (1 hunks)
  • src/pages/chat/ChatPageTest.tsx (1 hunks)
  • src/pages/onboarding/Onboarding.tsx (2 hunks)
🔇 Additional comments (4)
src/components/onboarding/SingleSelect.tsx (1)

2-8: 업종 옵션 데이터가 적절합니다.

한국 사업자 유형에 맞는 카테고리로 잘 업데이트되었습니다.

src/pages/chat/ChatPageTest.tsx (1)

119-129: LGTM!

파일 업로드 성공 메시지가 더 명확하고 사용자 친화적으로 개선되었습니다.

src/pages/onboarding/Onboarding.tsx (1)

13-13: 단계 수 증가 및 테마 색상 변경이 일관성 있게 적용되었습니다.

6단계로 확장된 온보딩 플로우와 #0D2D84 색상 테마가 FormButton, MultiSelect 등 다른 컴포넌트들과 일관되게 적용되어 있습니다.

Also applies to: 61-61

src/components/onboarding/FormButton.tsx (1)

3-3: LGTM!

버튼 색상이 새로운 테마 색상(#0D2D84)으로 일관성 있게 업데이트되었습니다. 이는 진행 상황 표시줄 및 선택된 옵션의 색상과 일치합니다.

Comment on lines +3 to +18
interface MultiSelectProps {
title: string;
options?: string[];
}

const MultiSelect = ({
title,
options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'],
}: MultiSelectProps) => {
const [selectedOptions, setSelectedOptions] = useState<string[]>([]);

const toggleOption = (option: string) => {
setSelectedOptions((prev) =>
prev.includes(option) ? prev.filter((item) => item !== option) : [...prev, option],
);
};
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

선택된 값을 부모 컴포넌트로 전달하는 방법이 없습니다.

selectedOptions 상태가 컴포넌트 내부에만 존재하고 부모로 노출되지 않아, Onboarding.tsx에서 사용자가 선택한 타겟층 데이터를 수집할 수 없습니다. 이는 폼 제출 시 필수 데이터가 누락되는 치명적인 문제입니다.

다음과 같이 onChange 콜백을 추가하세요:

 interface MultiSelectProps {
   title: string;
   options?: string[];
+  value?: string[];
+  onChange?: (selected: string[]) => void;
 }

 const MultiSelect = ({
   title,
   options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'],
+  value = [],
+  onChange,
 }: MultiSelectProps) => {
-  const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
+  const selectedOptions = value;

   const toggleOption = (option: string) => {
-    setSelectedOptions((prev) =>
-      prev.includes(option) ? prev.filter((item) => item !== option) : [...prev, option],
-    );
+    const newSelection = selectedOptions.includes(option)
+      ? selectedOptions.filter((item) => item !== option)
+      : [...selectedOptions, option];
+    onChange?.(newSelection);
   };
📝 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
interface MultiSelectProps {
title: string;
options?: string[];
}
const MultiSelect = ({
title,
options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'],
}: MultiSelectProps) => {
const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
const toggleOption = (option: string) => {
setSelectedOptions((prev) =>
prev.includes(option) ? prev.filter((item) => item !== option) : [...prev, option],
);
};
interface MultiSelectProps {
title: string;
options?: string[];
value?: string[];
onChange?: (selected: string[]) => void;
}
const MultiSelect = ({
title,
options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'],
value = [],
onChange,
}: MultiSelectProps) => {
const selectedOptions = value;
const toggleOption = (option: string) => {
const newSelection = selectedOptions.includes(option)
? selectedOptions.filter((item) => item !== option)
: [...selectedOptions, option];
onChange?.(newSelection);
};
🤖 Prompt for AI Agents
In src/components/onboarding/MultiSelect.tsx around lines 3 to 18, the component
keeps selectedOptions internal and never exposes changes to the parent; add an
onChange prop to MultiSelectProps (e.g., onChange?: (selected: string[]) =>
void), accept it in the component signature, and invoke it whenever selection
changes (either call onChange(updatedSelected) inside toggleOption after
updating state or use a useEffect watching selectedOptions to call onChange).
Ensure the prop is properly typed and has no-op fallback if undefined.

Comment on lines +24 to +39
{/* 6개 그리드 버튼 */}
<div className="grid grid-cols-2 gap-4 md:grid-cols-3">
{options.map((option, index) => (
<button
key={index}
onClick={() => toggleOption(option)}
className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${
selectedOptions.includes(option)
? 'border-[#0D2D84] bg-[#0D2D84] text-white'
: 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50'
}`}
>
{option}
</button>
))}
</div>
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

접근성 개선을 위해 버튼 상태 속성을 추가하세요.

선택 가능한 버튼들에 aria-pressed 속성이 없어 스크린 리더 사용자가 현재 선택 상태를 파악하기 어렵습니다.

다음과 같이 접근성 속성을 추가하세요:

         <button
           key={index}
           onClick={() => toggleOption(option)}
+          aria-pressed={selectedOptions.includes(option)}
           className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${
             selectedOptions.includes(option)
               ? 'border-[#0D2D84] bg-[#0D2D84] text-white'
               : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50'
           }`}
         >
📝 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
{/* 6개 그리드 버튼 */}
<div className="grid grid-cols-2 gap-4 md:grid-cols-3">
{options.map((option, index) => (
<button
key={index}
onClick={() => toggleOption(option)}
className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${
selectedOptions.includes(option)
? 'border-[#0D2D84] bg-[#0D2D84] text-white'
: 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50'
}`}
>
{option}
</button>
))}
</div>
{/* 6개 그리드 버튼 */}
<div className="grid grid-cols-2 gap-4 md:grid-cols-3">
{options.map((option, index) => (
<button
key={index}
onClick={() => toggleOption(option)}
aria-pressed={selectedOptions.includes(option)}
className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${
selectedOptions.includes(option)
? 'border-[#0D2D84] bg-[#0D2D84] text-white'
: 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50'
}`}
>
{option}
</button>
))}
</div>
🤖 Prompt for AI Agents
In src/components/onboarding/MultiSelect.tsx around lines 24 to 39, the
selectable buttons are missing ARIA state which makes it hard for screen reader
users to know which options are selected; add an aria-pressed attribute to each
button bound to the selection state (e.g.,
aria-pressed={selectedOptions.includes(option)}) so the button exposes its
pressed/selected state, and also ensure the element is a proper button
(type="button") to avoid form submission side-effects.

@github-actions
Copy link

github-actions bot commented Nov 8, 2025

@sunhwaaRj sunhwaaRj merged commit 7821606 into develop Nov 8, 2025
2 of 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.

2 participants