Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

경북대 FE_이정민 6주차 과제 Step1,2 #76

Open
wants to merge 19 commits into
base: userjmmm
Choose a base branch
from

Conversation

userjmmm
Copy link

@userjmmm userjmmm commented Aug 2, 2024

안녕하세요 멘토님,
백엔드와 API를 맞추면서 기능과 코드가 많이 수정되었습니다.
https://zzoe2346.github.io/api API 명세를 정리한 문서를 함께 첨부합니다!

배포는 네이버 클라우드 서버를 사용해서 진행했고, 배포된 사이트는 http://101.101.216.221:3000/ 에서 확인 가능합니다.

6주 동안 소중한 시간 내어 코드 리뷰를 제공해주셔서 감사합니다 :)

@sjoleee
Copy link

sjoleee commented Aug 4, 2024

안녕하세요~
충돌이 많이 발생하는데 해결해 주시면 좋을 것 같습니다~~
머지 후 step3 브랜치도 rebase해주시면 감사하겠습니다~!

userjmmm added 19 commits August 4, 2024 18:58
@userjmmm
Copy link
Author

userjmmm commented Aug 4, 2024

안녕하세요~ 충돌이 많이 발생하는데 해결해 주시면 좋을 것 같습니다~~ 머지 후 step3 브랜치도 rebase해주시면 감사하겠습니다~!

충돌 이슈를 해결 못 한 걸 모르고 PR을 올렸네요 죄송합니다ㅠㅠ. 현재 충돌 해결 후 step3 브랜치도 rebase한 상태입니다. �알려주셔서 감사합니다!

Copy link

@sjoleee sjoleee left a comment

Choose a reason for hiding this comment

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

�수고하셨습니다~! 👍
리뷰 남깁니다!

Comment on lines +5 to +6
let BASE_URL = localStorage.getItem('baseURL') || 'https://api.example.com';

Copy link

Choose a reason for hiding this comment

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

let으로 선언하는 이유가 무엇일까요?
BASE_URL이 변경될 수 있는건가요?
해당 파일 내에서 let이 자주 사용되는데, 변경의 추적이 어려우므로 const를 사용해 주세요!

Comment on lines +38 to +50
export const getCategories = async (): Promise<CategoryData[]> => {
let allCategories: CategoryData[] = [];
let currentPage = 0;
let hasNextPage = true;

export const getCategoriesPath = () => `${BASE_URL}/api/categories`;
const categoriesQueryKey = [getCategoriesPath()];
while (hasNextPage) {
const response = await fetchInstance.get<CategoryResponseData>(
`${getCategoriesPath()}?page=${currentPage}&size=100`
);
allCategories = [...allCategories, ...response.data.content];
hasNextPage = !response.data.last;
currentPage++;
}
Copy link

Choose a reason for hiding this comment

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

currentPage, hasNextPage가 getCategories 함수의 책임일까요?

Comment on lines +28 to +51
const [counts, setCounts] = useState<CountState>(() => {
const initialCounts: CountState = {};
if (options) {
options.forEach(option => {
initialCounts[option.id] = '0';
});
}
return initialCounts;
});

useEffect(() => {
if (options) {
setCounts(prevCounts => {
const newCounts = { ...prevCounts };
options.forEach(option => {
if (!newCounts[option.id]) {
newCounts[option.id] = '0';
}
});
return newCounts;
});
}
}, [options]);

Copy link

Choose a reason for hiding this comment

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

해당 작업이 어떤 역할인지 잘 모르겠네요!
알아볼 수 있도록 적절한 함수명을 가진 함수로 분리하여 사용하거나, 셀프 리뷰를 통해 미리 어떤 역할의 함수인지 코멘트를 남겨두는 방법도 있으니 사용해 보시면 좋을 것 같습니다~

Comment on lines 71 to 86
try {
const response = await fetch('/api/wishes', {
const requestBody = {
productId: parseInt(productId, 10),
quantity: null,
};

console.log('Sending request to add wish', requestBody); // 잘 받아오는지 확인 - 추후 삭제 예정

const response = await fetch(`${getBaseUrl()}/api/wishes`, {
method: 'POST',
headers: {
Authorization: `Bearer ${authInfo.token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
productId: parseInt(productId, 10),
name: detail?.name,
price: detail?.price,
imageUrl: detail?.imageUrl,
}),
body: JSON.stringify(requestBody),
});
Copy link

Choose a reason for hiding this comment

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

이 부분은 왜 react-query, axios를 쓰지 않고 fetch를 쓰셨나요?

Comment on lines +13 to +33
const [selectedApi, setSelectedApi] = useState('default');

useEffect(() => {
const storedBaseUrl = localStorage.getItem('baseURL');
if (storedBaseUrl) {
setSelectedApi(storedBaseUrl);
updateBaseUrl(storedBaseUrl);
}
}, []);

const handleLogin = () => {
navigate(getDynamicPath.login());
};

const handleApiChange = (event: ChangeEvent<HTMLSelectElement>) => {
const newApi = event.target.value;
setSelectedApi(newApi);
localStorage.setItem('baseURL', newApi);
updateBaseUrl(newApi);
queryClient.invalidateQueries();
};
Copy link

Choose a reason for hiding this comment

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

아래 코드를 보니 여러 명이 개발한 api를 사용하기 위해서 작성해두신 것으로 보이네요.
다만 제가 느끼기에는 그냥 instance에 base url을 선언하고 쓰지 않고. 전체 url을 그때그때 받아서 사용하는 방식으로 쓰는게 나을 것 같습니다.
base url이 변경되는 것에 영향을 받는 코드가 프로젝트 여기저기에 퍼져있어 좋지 않을 것 같아요

Comment on lines +41 to 95
const orders = orderHistorySessionStorage.get() as OrderHistory[];

const requests = orders.map(order => {
const requestBody = {
optionId: order.optionId,
quantity: order.quantity,
message: values.message,
};

console.log('서버로 전송하는 요청 데이터:', JSON.stringify(requestBody, null, 2));

return fetch(`${getBaseUrl()}/api/orders`, {
method: 'POST',
headers: {
Authorization: `Bearer ${authInfo?.token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
credentials: 'include',
})
.then(async response => {
if (!response.ok) {
const errorBody = await response.text();
console.error(`옵션 ID ${order.optionId} 주문 실패:`, response.status, errorBody);
return { success: false, optionId: order.optionId, error: errorBody };
}
const data = await response.json();
console.log(`옵션 ID ${order.optionId} 주문 성공:`, data);
return { success: true, optionId: order.optionId, data };
})
.catch(error => {
console.error(`옵션 ID ${order.optionId} 주문 에러:`, error);
return { success: false, optionId: order.optionId, error: error.message };
});
});

try {
const results = await Promise.all(requests);
const successfulOrders = results.filter(result => result.success);
const failedOrders = results.filter(result => !result.success);

if (successfulOrders.length > 0) {
console.log('성공한 주문:', successfulOrders);
alert(`${successfulOrders.length}개의 주문이 완료되었습니다.`);
}

if (failedOrders.length > 0) {
console.error('실패한 주문:', failedOrders);
alert(`${failedOrders.length}개의 주문 중 오류가 발생했습니다.`);
}
} catch (error) {
console.error('주문 처리 중 예외 발생:', error);
alert('주문 처리 중 예기치 않은 오류가 발생했습니다.');
}
};
Copy link

Choose a reason for hiding this comment

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

해당 함수의 가독성이 좋지 않네요...
분기가 매우 많고, 명령형으로 처리되고 있으니 개선해 보시면 좋을 것 같아요~

Comment on lines +50 to +74
const [userName, setUserName] = useState<string>("사용자");

useEffect(() => {
const fetchWishlist = async () => {
if (authInfo) {
try {
const response = await fetch(`/api/wishes?page=${page}&size=10&sort=createdDate,desc`, {
const [headerEncoded, payloadEncoded, signature] = authInfo.split('.');
const payload = JSON.parse(base64.decode(payloadEncoded));

console.log('Token Header:', JSON.parse(base64.decode(headerEncoded)));
console.log('Token Payload:', payload);
console.log('Token Signature:', signature);
console.log('Payload Keys:', Object.keys(payload));

if (payload.sub) {
const email = payload.sub;
const displayName = email.split('@')[0];
setUserName(displayName);
}
} catch (error) {
console.error('토큰 디코딩 에러:', error);
}
} else {
setUserName("사용자");
}
}, [authInfo]);
Copy link

Choose a reason for hiding this comment

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

토큰을 클라이언트에서 디코드하여 유저 정보를 확인하는 것으로 보여요.
이건 백엔드와 협의했다면 어쩔 수 없는 부분이니 넘어가고...
디코딩하는 부분을 추상화하면 어떨까 싶네요!

Comment on lines +76 to +104
const getWishlistPath = () => `${getBaseUrl()}/api/wishes`;

const fetchWishlist = async (page: number) => {
try {
const response = await fetchInstance.get<WishlistResponseData>(
`${getWishlistPath()}?page=${page}&size=10&sort=createdDate,desc`,
{
headers: {
Authorization: `Bearer ${authInfo.token}`,
Authorization: `Bearer ${authInfo}`,
},
});

if (response.status === 200) {
const data = await response.json();
setWishlist(data.content);
setTotalPages(data.totalPages);
} else if (response.status === 401) {
alert('토큰이 유효하지 않습니다. 다시 로그인해주세요.');
} else {
alert('위시 리스트를 불러오는데 실패했습니다.');
withCredentials: true,
}
} catch (error) {
console.error('위시 리스트 조회 에러:', error);
alert('위시 리스트 조회 에러가 발생했습니다.');
);

if (response.status === 200) {
const data = response.data;
console.log('Response data:', data);
setWishlist(data.content);
setTotalPages(data.totalPages);
} else if (response.status === 403) {
alert('토큰이 유효하지 않습니다. 다시 로그인해주세요.');
} else {
alert('위시 리스트를 불러오는데 실패했습니다.');
}
};
} catch (error) {
console.error('위시 리스트 조회 에러:', error);
alert('위시 리스트 조회 에러가 발생했습니다.');
}
};
Copy link

Choose a reason for hiding this comment

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

여기는 왜 react-query를 사용하지 않았나요?

Comment on lines +106 to +108
useEffect(() => {
fetchWishlist(page);
}, [page, authInfo]);
Copy link

Choose a reason for hiding this comment

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

useEffect의 의존성 배열 규칙을 지켜주세요~!!

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