Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/api/datetest/datetest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { TDateTestQuestion, TRelationTypeResponse } from '@/types/datetest/datetest';
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

응답 타입 import 보강 제안 (POST 결과 타입 명시용)

POST 응답 결과 타입을 명확히 다루기 위해 TDateTestResultResponse(존재한다면)를 함께 import하는 것을 권장합니다. 없다면 타입 정의에 추가하거나, 아래 다른 코멘트의 대안(인라인 결과 타입)으로 가도 됩니다.

다음 diff는 TDateTestResultResponse가 이미 존재한다는 가정하에 제안합니다:

-import type { TDateTestQuestion, TRelationTypeResponse } from '@/types/datetest/datetest';
+import type { TDateTestQuestion, TRelationTypeResponse, TDateTestResultResponse } from '@/types/datetest/datetest';

🏁 Script executed:

#!/bin/bash
grep -R "TDateTestResultResponse" -n src/types/datetest

Length of output: 161


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' src/api/datetest/datetest.ts

Length of output: 1309


src/api/datetest/datetest.ts – POST 응답 타입 보강 제안

TDateTestResultResponse가 이미 정의되어 있으므로, 상단 import에 함께 추가한 뒤 POST 요청 시 제네릭으로 지정해 응답 타입 안정성을 높이는 것을 권장합니다:

-import type { TDateTestQuestion, TRelationTypeResponse } from '@/types/datetest/datetest';
+import type { TDateTestQuestion, TRelationTypeResponse, TDateTestResultResponse } from '@/types/datetest/datetest';

추가로, POST 호출부를 아래처럼 수정하면 좋습니다:

- const { data } = await axiosInstance.post('/api/v1/dates/preferences/tests', payload);
+ const { data } = await axiosInstance.post<TDateTestResultResponse>('/api/v1/dates/preferences/tests', payload);
📝 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
import type { TDateTestQuestion, TRelationTypeResponse } from '@/types/datetest/datetest';
// src/api/datetest/datetest.ts
-import type { TDateTestQuestion, TRelationTypeResponse } from '@/types/datetest/datetest';
+import type { TDateTestQuestion, TRelationTypeResponse, TDateTestResultResponse } from '@/types/datetest/datetest';
...
-export const submitDateTestAnswers = async (payload: { answers: number[] }) => {
- const { data } = await axiosInstance.post('/api/v1/dates/preferences/tests', payload);
+export const submitDateTestAnswers = async (payload: { answers: number[] }) => {
+ const { data } = await axiosInstance.post<TDateTestResultResponse>('/api/v1/dates/preferences/tests', payload);
if (!data.isSuccess) {
throw new Error(data.message || '답안 제출에 실패했습니다');
}
return data.result;
};
🤖 Prompt for AI Agents
In src/api/datetest/datetest.ts around line 1, the file imports
TDateTestQuestion and TRelationTypeResponse but omits TDateTestResultResponse;
add TDateTestResultResponse to the import list and then pass it as the generic
type to the POST request call so the response is strongly typed (e.g., update
the POST call to use the HTTP client’s generic parameter with
TDateTestResultResponse and adjust any variable typings accordingly).


import { axiosInstance } from '../axiosInstance';

export const getDateQuestions = async () => {
const res = await axiosInstance.get<TDateTestQuestion>('/api/v1/dates/preferences/questions');
if (!res.data.isSuccess) {
throw new Error(res.data.message || '질문을 불러오는데 실패했습니다');
}
return res.data.result.questions;
};
Comment on lines +5 to +11
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

성공/실패 분기 추가는 좋습니다. 반환 타입 명시로 API 계약을 더 분명히 해주세요

현재도 타입 추론은 되지만, 외부에 공개되는 함수 시그니처를 명시하면 유지보수성이 좋아집니다.

다음처럼 반환 타입을 명시해 주세요:

-export const getDateQuestions = async () => {
+export const getDateQuestions = async (): Promise<TDateTestQuestion['result']['questions']> => {
📝 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
export const getDateQuestions = async () => {
const res = await axiosInstance.get<TDateTestQuestion>('/api/v1/dates/preferences/questions');
if (!res.data.isSuccess) {
throw new Error(res.data.message || '질문을 불러오는데 실패했습니다');
}
return res.data.result.questions;
};
export const getDateQuestions = async (): Promise<TDateTestQuestion['result']['questions']> => {
const res = await axiosInstance.get<TDateTestQuestion>('/api/v1/dates/preferences/questions');
if (!res.data.isSuccess) {
throw new Error(res.data.message || '질문을 불러오는데 실패했습니다');
}
return res.data.result.questions;
};
🤖 Prompt for AI Agents
In src/api/datetest/datetest.ts around lines 5 to 11, the exported async
function lacks an explicit return type; add a Promise return type that matches
the API contract (for example Promise<TDateTestQuestion['result']['questions']>)
on the function signature, keep the axios call generic as-is, and ensure
TDateTestQuestion is imported/available so the function signature reads: export
const getDateQuestions = async ():
Promise<TDateTestQuestion['result']['questions']> => { ... }.


export const submitDateTestAnswers = async (payload: { answers: number[] }) => {
const { data } = await axiosInstance.post('/api/v1/dates/preferences/tests', payload);
if (!data.isSuccess) {
throw new Error(data.message || '답변 제출에 실패했습니다');
}
return data;
};
Comment on lines +13 to +19
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

응답 처리 일관성: result만 반환하도록 통일 + POST 제네릭/반환 타입 지정

이 모듈에서 일부 함수는 result만, 일부는 전체 data를 반환합니다. 일관성을 위해 result만 반환하도록 통일하는 것을 권장합니다. 또한 POST 응답 제네릭을 지정해 타입 안정성을 높여주세요.

다음 diff는 TDateTestResultResponse가 존재한다고 가정합니다(없다면 코멘트 하단의 대안 참고):

-export const submitDateTestAnswers = async (payload: { answers: number[] }) => {
-    const { data } = await axiosInstance.post('/api/v1/dates/preferences/tests', payload);
-    if (!data.isSuccess) {
-        throw new Error(data.message || '답변 제출에 실패했습니다');
-    }
-    return data;
-};
+export const submitDateTestAnswers = async (
+    payload: { answers: number[] }
+): Promise<TDateTestResultResponse['result']> => {
+    const { data } = await axiosInstance.post<TDateTestResultResponse>(
+        '/api/v1/dates/preferences/tests',
+        payload
+    );
+    if (!data.isSuccess) {
+        throw new Error(data.message || '답변 제출에 실패했습니다');
+    }
+    return data.result;
+};

대안: TDateTestResultResponse 타입이 없다면, 임시로 아래처럼 결과 타입을 인라인으로 명시할 수 있습니다.

type DateTestSubmitResult = TDateTestQuestion['result']; // 혹은 실제 결과 스키마에 맞는 타입
🤖 Prompt for AI Agents
In src/api/datetest/datetest.ts around lines 13 to 19, the function currently
returns the whole response data and lacks a POST generic; change it to use
axiosInstance.post<TDateTestResultResponse> to type the response, update the
function signature to return the result type (e.g.
Promise<TDateTestResultResponse>) and, on success, return data.result instead of
data; keep the existing error throw but preserve data.message for failure. If
TDateTestResultResponse doesn't exist, create a local type alias (e.g.
DateTestSubmitResult) matching the result shape and use that as the generic and
return type.


export const postDateTestResult = async (answers: number[]): Promise<TDateTestQuestion> => {
const res = await axiosInstance.post('/api/v1/dates/preferences/tests', { answers });
return res.data.result;
};

export const getRelationTypes = async (type: string): Promise<TRelationTypeResponse> => {
const res = await axiosInstance.get(`/api/v1/dates/preferences/relations`, {
params: { type },
});
if (!res.data.isSuccess) {
throw new Error(res.data.message || '관계 유형을 불러오는데 실패했습니다');
}
return res.data;
};
Comment on lines +26 to +34
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

axios 제네릭 누락 및 반환 형태 일관성 개선

GET 응답 제네릭을 지정하면 내부에서도 정확한 타입을 얻을 수 있습니다. 또한 모듈 내 일관성을 위해 result만 반환하는 규약으로 맞추는 것을 권장합니다.

다음과 같이 수정해 주세요:

-export const getRelationTypes = async (type: string): Promise<TRelationTypeResponse> => {
-    const res = await axiosInstance.get(`/api/v1/dates/preferences/relations`, {
-        params: { type },
-    });
-    if (!res.data.isSuccess) {
-        throw new Error(res.data.message || '관계 유형을 불러오는데 실패했습니다');
-    }
-    return res.data;
-};
+export const getRelationTypes = async (
+    type: string
+): Promise<TRelionTypeResponse['result']> => {
+    const res = await axiosInstance.get<TRelationTypeResponse>(
+        `/api/v1/dates/preferences/relations`,
+        { params: { type } }
+    );
+    if (!res.data.isSuccess) {
+        throw new Error(res.data.message || '관계 유형을 불러오는데 실패했습니다');
+    }
+    return res.data.result;
+};

참고: 위 변경이 어렵다면 최소한 axiosInstance.get<TRelationTypeResponse>(...) 제네릭 지정은 적용해 주세요.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/api/datetest/datetest.ts around lines 26-34, the axios GET call is
missing a response generic and the function returns the whole res.data instead
of the module's convention of returning only result; update the call to
axiosInstance.get<TRelationTypeResponse>(...) so TypeScript infers the correct
shape, change the function signature to return the type of res.data.result (e.g.
Promise<TRelationType[]> or the appropriate result type), and return
res.data.result (keeping the existing error throw using res.data.message or the
fallback message).

63 changes: 39 additions & 24 deletions src/pages/dateTest/DateTestStep.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';

interface IQuestion {
id: number;
title: string;
options: string[];
}
import type { IDateTestQuestion } from '@/types/datetest/datetest';

import { getDateQuestions, submitDateTestAnswers } from '@/api/datetest/datetest';

const TOTAL_QUESTIONS = 40;
const dummyQuestions: IQuestion[] = Array.from({ length: TOTAL_QUESTIONS }, (_, i) => ({
id: i + 1,
title: `당신의 데이트 스타일은 어떤가요?`,
options: ['야외 활동 좋아해요', '집에서 쉬는 게 좋아요'],
}));

function ProgressBar({ step, total }: { step: number; total: number }) {
const percentage = (step / total) * 100;
Expand All @@ -26,19 +20,29 @@ function ProgressBar({ step, total }: { step: number; total: number }) {
export default function DateTestStep() {
const { step } = useParams<{ step: string }>();
const navigate = useNavigate();

const currentStep = Number(step);
const question = dummyQuestions[currentStep - 1];
const currentStep = parseInt(step ?? '1', 10);

const [selectedOptions, setSelectedOptions] = useState<number[]>([]);
const [allAnswers, setAllAnswers] = useState<number[]>(Array(TOTAL_QUESTIONS).fill(0));

const { data, isLoading, isError } = useQuery<IDateTestQuestion[]>({
queryKey: ['dateTestQuestions'],
queryFn: getDateQuestions,
staleTime: 1000 * 60 * 5,
});

useEffect(() => {
setSelectedOptions([]);
const prevAnswer = allAnswers[currentStep - 1];
setSelectedOptions(prevAnswer ? [prevAnswer - 1] : []);
window.scrollTo(0, 0);
}, [currentStep]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

useEffect 의존성 배열 누락

useEffect에서 allAnswers를 사용하지만 의존성 배열에 포함되지 않았습니다.

-    }, [currentStep]);
+    }, [currentStep, allAnswers]);
📝 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
}, [currentStep]);
// …inside your useEffect…
}, [currentStep, allAnswers]);
🤖 Prompt for AI Agents
In src/pages/dateTest/DateTestStep.tsx around line 38, the useEffect uses
allAnswers but does not include it in the dependency array; add allAnswers to
the dependency array (or memoize/stabilize it) so the effect re-runs when
answers change, and if adding it could cause unwanted loops ensure the effect
logic uses stable references or guards (e.g., compare previous value or
useCallback/useMemo) to prevent infinite re-renders.


const handleSelect = (idx: number) => {
setSelectedOptions([idx]); // 항상 하나만 선택되도록
const answerValue = idx + 1; // 0 -> 1, 1 -> 2
const updatedAnswers = [...allAnswers];
updatedAnswers[currentStep - 1] = answerValue;
setAllAnswers(updatedAnswers);
setSelectedOptions([idx]);
};

const handlePrev = () => {
Expand All @@ -49,18 +53,26 @@ export default function DateTestStep() {
}
};

const handleNext = () => {
const handleNext = async () => {
if (selectedOptions.length !== 1) return;

if (currentStep < TOTAL_QUESTIONS) {
navigate(`/datetest/${currentStep + 1}`);
} else {
navigate('/datetest/result');
try {
const response = await submitDateTestAnswers({ answers: allAnswers });
navigate('/datetest/result', { state: response.result });
} catch (error) {
console.error('결과 제출 실패:', error);
alert('결과를 제출하는 중 오류가 발생했습니다.');
}
}
};

if (!question) {
return <div>질문을 불러올 수 없습니다.</div>;
}
if (isLoading) return <div className="text-center mt-20">질문을 불러오는 중입니다...</div>;
if (isError || !data || !data[currentStep - 1]) return <div>질문을 불러올 수 없습니다.</div>;

const question = data[currentStep - 1];

return (
<div className="flex flex-col px-6 max-w-3xl mx-auto py-[156px]">
Expand Down Expand Up @@ -88,16 +100,19 @@ export default function DateTestStep() {
</div>

{/* 질문 */}
<h2 className="text-2xl font-bold mb-6 text-left w-full">{question.title}</h2>
<h2 className="text-2xl font-bold mb-6 text-left w-full">{question.question}</h2>

{/* 선택지 */}
<div className="flex flex-col gap-4 w-full">
{question.options.map((opt, idx) => (
{[question.firstAnswer, question.secondAnswer].map((opt, idx) => (
<button
key={idx}
onClick={() => handleSelect(idx)}
className={`w-full text-left px-5 py-3 rounded-lg border border-[#C3C3C3]
${selectedOptions.includes(idx) ? 'bg-primary-700 text-white border-primary-700' : 'bg-white text-gray-800 hover:bg-primary-100 border border-[#c3c3c3]'}`}
className={`w-full text-left px-5 py-3 rounded-lg border ${
selectedOptions.includes(idx)
? 'bg-primary-700 text-white border-primary-700'
: 'bg-white text-gray-800 hover:bg-primary-100 border-[#c3c3c3]'
}`}
aria-pressed={selectedOptions.includes(idx)}
type="button"
>
Expand Down
Loading