Skip to content
Open
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
54 changes: 22 additions & 32 deletions src/apis/auth/authApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ import axiosInstance from '@/apis/instance';
export const getKakaoLoginUrl = () => {
const clientId = import.meta.env.VITE_KAKAO_CLIENT_ID;
const redirectUri = import.meta.env.VITE_KAKAO_REDIRECT_URI;

return `https://kauth.kakao.com/oauth/authorize?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code`;
};

// 카카오 액세스 토큰 받기
export const getKakaoAccessToken = async (code) => {
console.log('카카오 액세스 토큰 요청:', { code });

const response = await fetch('https://kauth.kakao.com/oauth/token', {
method: 'POST',
headers: {
Expand All @@ -32,27 +30,22 @@ export const getKakaoAccessToken = async (code) => {
}

const data = await response.json();
console.log('카카오 액세스 토큰 발급 성공');
return data.access_token;
};

// 카카오 로그인 (액세스 토큰으로 로그인)
// 카카오 로그인
export const kakaoLogin = async (kakaoAccessToken, locationConsent = true) => {
const requestData = {
kakaoAccessToken: kakaoAccessToken,
location_consent: locationConsent,
location_consent_version: "1.0"
location_consent_version: '1.0',
};

console.log('카카오 로그인 요청 전체 데이터:', requestData);
console.log('액세스 토큰 길이:', kakaoAccessToken?.length);
console.log('액세스 토큰 타입:', typeof kakaoAccessToken);
console.log('location_consent 타입:', typeof locationConsent);
console.log('location_consent_version 타입:', typeof "1.0");


try {
const response = await axiosInstance.post('/api/auth/kakao/login', requestData);
console.log('카카오 로그인 성공:', response.data);
const response = await axiosInstance.post(
'/api/auth/kakao/login',
requestData
);
return response.data;
} catch (error) {
console.error('카카오 로그인 API 에러:', error);
Expand All @@ -68,14 +61,12 @@ export const getGoogleLoginUrl = () => {
const clientId = import.meta.env.VITE_GOOGLE_CLIENT_ID;
const redirectUri = import.meta.env.VITE_GOOGLE_REDIRECT_URI;
const scope = 'openid email profile';

return `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}`;
};

// 구글 액세스 토큰 받기
export const getGoogleAccessToken = async (code) => {
console.log('구글 액세스 토큰 요청:', { code });

const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: {
Expand All @@ -97,22 +88,18 @@ export const getGoogleAccessToken = async (code) => {
}

const data = await response.json();
console.log('구글 액세스 토큰 발급 성공');
return data.access_token;
};

// 구글 로그인 (액세스 토큰으로 로그인)
export const googleLogin = async (googleAccessToken, locationConsent = true) => {
console.log('구글 로그인 요청:', {
googleAccessToken: googleAccessToken?.substring(0, 10) + '...',
location_consent: locationConsent,
location_consent_version: "1.0"
});

// 구글 로그인
export const googleLogin = async (
googleAccessToken,
locationConsent = true
) => {
const response = await axiosInstance.post('/api/auth/google/login', {
googleAccessToken: googleAccessToken,
location_consent: locationConsent,
location_consent_version: "1.0"
location_consent_version: '1.0',
});
return response.data;
};
Expand All @@ -126,7 +113,7 @@ export const logout = async () => {
// 토큰 갱신
export const refreshToken = async (refreshToken) => {
const response = await axiosInstance.post('/api/auth/refresh', {
refresh_token: refreshToken
refresh_token: refreshToken,
});
return response.data;
};
Expand All @@ -138,10 +125,13 @@ export const getUserInfo = async () => {
};

// 위치정보 동의 업데이트
export const updateLocationConsent = async (locationConsent, version = "1.0") => {
export const updateLocationConsent = async (
locationConsent,
version = '1.0'
) => {
const response = await axiosInstance.patch('/api/users/me/location-consent', {
location_consent: locationConsent,
location_consent_version: version
location_consent_version: version,
});
return response.data;
};
Expand All @@ -156,4 +146,4 @@ export const deleteUser = async () => {
export const getMyToilets = async () => {
const response = await axiosInstance.get('/api/users/me/toilets');
return response.data;
};
};
27 changes: 3 additions & 24 deletions src/apis/instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,15 @@ const axiosInstance = axios.create({
headers: {
'Content-Type': 'application/json',
},
paramsSerializer: (params) => qs.stringify(params, { arrayFormat: 'repeat' }), // 배열을 repeat 방식으로 직렬화
paramsSerializer: (params) => qs.stringify(params, { arrayFormat: 'repeat' }),
});

// 요청 인터셉터 (디버깅 로그 추가)
// 요청 인터셉터
axiosInstance.interceptors.request.use(
(config) => {
console.log('axios 요청:', {
method: config.method?.toUpperCase(),
url: config.url,
baseURL: config.baseURL,
headers: config.headers,
params: config.params,
data: config.data,
});

// 인증이 필요한 요청에 토큰 추가
const token = getAccessToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
console.log('토큰 추가됨:', `Bearer ${token.substring(0, 10)}...`);
}
return config;
},
Expand All @@ -52,10 +41,6 @@ axiosInstance.interceptors.request.use(
// 응답 인터셉터
axiosInstance.interceptors.response.use(
(response) => {
console.log('axios 응답 성공:', {
status: response.status,
data: response.data,
});
return response;
},
async (error) => {
Expand All @@ -67,12 +52,10 @@ axiosInstance.interceptors.response.use(

const originalRequest = error.config;

// 401 에러 처리 (토큰 만료 등)
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;

try {
// 리프레시 토큰 가져오기
const refreshToken = (() => {
const nameEQ = 'refresh_token=';
const ca = document.cookie.split(';');
Expand All @@ -96,19 +79,15 @@ axiosInstance.interceptors.response.use(

if (refreshResponse.data.statusCode === 200) {
const newAccessToken = refreshResponse.data.data.access_token;

// 새 토큰을 쿠키에 저장
const expires = new Date();
expires.setTime(expires.getTime() + 24 * 60 * 60 * 1000); // 1일
expires.setTime(expires.getTime() + 24 * 60 * 60 * 1000);
document.cookie = `access_token=${newAccessToken};expires=${expires.toUTCString()};path=/;SameSite=Strict;Secure`;

// 원래 요청에 새 토큰 설정
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
return axiosInstance(originalRequest);
}
} catch (refreshError) {
console.error('토큰 갱신 실패:', refreshError);
// 리프레시 실패 시 로그아웃 처리
document.cookie =
'access_token=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;';
document.cookie =
Expand Down
23 changes: 13 additions & 10 deletions src/apis/register/registerApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,23 @@ export const createToilet = async (toiletData) => {
* @param {FormData} imageData - 'files'를 키로 하는 이미지 파일 데이터
*/
export const uploadToiletImages = async (imageData) => {
const response = await axiosInstance.post(`/api/toilets/images/upload`, imageData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
const response = await axiosInstance.post(
`/api/toilets/images/upload`,
imageData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
}
);
return response.data;
};

/**
* 화장실 이미지 단건 삭제
* @param {number} imageId - 이미지 ID (파라미터명 수정)
* @param {number} imageId - 이미지 ID
*/
export const deleteToiletImage = async (imageId) => { // ✅ image_id → imageId로 변경
console.log('deleteToiletImage 호출됨, imageId:', imageId);
const response = await axiosInstance.delete(`/api/toilets/images/${imageId}`); // ✅ 변수명도 변경
export const deleteToiletImage = async (imageId) => {
const response = await axiosInstance.delete(`/api/toilets/images/${imageId}`);
return response.data;
};
};
37 changes: 18 additions & 19 deletions src/components/mypage/ReviewCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@ const ReviewCard = ({ review, onDelete }) => {
const renderStars = (rating) => {
return Array.from({ length: 5 }, (_, index) => {
const StarComponent = index < rating ? StarBlue : StarGray;
return (
<StarComponent
key={index}
className="w-6 h-6"
/>
);
return <StarComponent key={index} className="w-6 h-6" />;
});
};

Expand All @@ -23,32 +18,31 @@ const ReviewCard = ({ review, onDelete }) => {
};

const handleDelete = () => {
console.log('리뷰 삭제:', review.id);
if (onDelete) {
onDelete(review.id);
}
};

return (
<div className="border-t border-b border-gray-1 px-14 py-12">
{/* 화장실 이름과 삭제 버튼 */}
{/* 화장실 이름 + 삭제 버튼 */}
<div className="flex justify-between items-center mb-4">
<button
<button
onClick={handleToiletClick}
className="flex justify-center items-center text-heading3-bold text-gray-10 hover:text-main transition-colors cursor-pointer"
>
{review.toiletName}
<Arrow className="inline-block ml-4 w-4 h-4" />
</button>
<button
<button
onClick={handleDelete}
className="px-6 py-2 bg-gray-0 text-gray-4 rounded border border-gray-4 hover:bg-gray-4 transition-colors"
>
삭제
</button>
</div>

{/* 별점과 작성자 정보 */}
{/* 프로필 + 별점 */}
<div className="flex items-center gap-4 mb-2">
<div className="w-[34px] h-[34px] bg-main rounded-full flex items-center justify-center">
<span className="text-white text-body2-bold">화</span>
Expand All @@ -62,15 +56,20 @@ const ReviewCard = ({ review, onDelete }) => {
</div>
</div>
</div>
{/* 작성 날짜 */}

{/* 작성 날짜 */}
<div className="text-body2 text-gray-6 mb-4">{review.createdAt}</div>
{/* 이미지들 */}

{/* 첨부 이미지 */}
{review.images && review.images.length > 0 && (
<div className="flex gap-4 mb-4">
{review.images.map((image, index) => (
<div key={index} className="w-[180px] h-[180px] bg-gray-1 rounded overflow-hidden">
<img
src={image}
<div
key={index}
className="w-[180px] h-[180px] bg-gray-1 rounded overflow-hidden"
>
<img
src={image}
alt={`리뷰 이미지 ${index + 1}`}
className="w-full h-full object-cover"
/>
Expand All @@ -79,10 +78,10 @@ const ReviewCard = ({ review, onDelete }) => {
</div>
)}

{/* 리뷰 내용 */}
{/* 리뷰 본문 */}
<p className="text-body1 text-gray-6 mb-4">{review.content}</p>

{/* 태그들 */}
{/* 태그 */}
<div className="flex gap-4">
{review.tags.map((tag, index) => (
<div key={index} className="px-6 py-2 bg-gray-0 rounded-[50px]">
Expand All @@ -94,4 +93,4 @@ const ReviewCard = ({ review, onDelete }) => {
);
};

export default ReviewCard;
export default ReviewCard;
Loading