-
Notifications
You must be signed in to change notification settings - Fork 26
Fe5 #102
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
Fe5 #102
Conversation
|
스프리트 미션 하시느라 수고 많으셨어요. |
페이지네이션은 혼자 못해서 검색과 ai를 활용해 하였습니다페이지네이션 처음 개발하려 하면 어렵죠 ㅎㅎㅎ. 결국 문제 해결할 방법을 찾아서 해결하셨다는 점은 훌륭합니다 ! 다만 AI로 산출된 코드는 모두 이해하셔야 합니다 ! 또한 가장 권장드리고 싶은건 문제를 직접 해결해보시고 AI가 필요하다면 디버깅 방법을 물어보시는 것도 좋은 방법입니다. 😉 |
앞으로 제출하실 때 PR 이름은
|
|
|
||
| function Items() { | ||
| const [order, setOrder] = useState("createdAt"); | ||
| const [fourItems, setFourItems] = useState([]); | ||
| const [items, setItems] = useState([]); | ||
| const [totalPages, setTotalPages] = useState(1); | ||
| const [currentPage, setCurrentPage] = useState(1); | ||
| const [itemsPerPage, setItemPerPage] = useState(10); // 한 페이지에 10개 | ||
| const [bestItemPerPage, setBestItemPerPage] = useState(4); | ||
| const pageGroupSize = 5; // 페이지 버튼 5개씩 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
상수는 컴포넌트 밖에서 선언해볼 수 있어요 😉
| function Items() { | |
| const [order, setOrder] = useState("createdAt"); | |
| const [fourItems, setFourItems] = useState([]); | |
| const [items, setItems] = useState([]); | |
| const [totalPages, setTotalPages] = useState(1); | |
| const [currentPage, setCurrentPage] = useState(1); | |
| const [itemsPerPage, setItemPerPage] = useState(10); // 한 페이지에 10개 | |
| const [bestItemPerPage, setBestItemPerPage] = useState(4); | |
| const pageGroupSize = 5; // 페이지 버튼 5개씩 | |
| const PAGE_GROUP_SIZE = 5; | |
| function Items() { | |
| const [order, setOrder] = useState("createdAt"); | |
| const [fourItems, setFourItems] = useState([]); | |
| const [items, setItems] = useState([]); | |
| const [totalPages, setTotalPages] = useState(1); | |
| const [currentPage, setCurrentPage] = useState(1); | |
| const [itemsPerPage, setItemPerPage] = useState(10); // 한 페이지에 10개 | |
| const [bestItemPerPage, setBestItemPerPage] = useState(4); |
컴포넌트 내부에 있으면 리렌더링에 의한 불필요한 재선언이 될 수 있으며, 해당 컴포넌트의 자원을 사용하지 않는 함수 및 변수를 밖에서 선언해준다면 자연스럽게 컴포넌트는 필요한 자원들만 구성되게 됩니다. 😊
| useEffect(() => { | ||
| const handleResize = () => { | ||
| if (window.innerWidth < 768) { | ||
| console.log("📱 모바일"); | ||
| setItemPerPage(4); | ||
| setBestItemPerPage(1); | ||
| setCurrentPage(1); | ||
| } else if (window.innerWidth < 1025) { | ||
| console.log("📲 태블릿"); | ||
| setItemPerPage(6); | ||
| setBestItemPerPage(2); | ||
| setCurrentPage(1); | ||
| } else { | ||
| console.log("💻 데스크탑"); | ||
| setItemPerPage(10); | ||
| setBestItemPerPage(4); | ||
| setCurrentPage(1); | ||
| } | ||
| }; | ||
|
|
||
| handleResize(); // 최초 실행 | ||
| window.addEventListener("resize", handleResize); | ||
|
|
||
| return () => { | ||
| window.removeEventListener("resize", handleResize); | ||
| }; | ||
| }, []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(심화/응용) 해당 useEffect은 hook을 활용해볼 수도 있겠군요 ! 😉
import { useEffect, useState } from "react";
export default function useResponsivePagination() {
const [config, setConfig] = useState({
itemPerPage: 10,
bestItemPerPage: 4,
});
useEffect(() => {
const handleResize = () => {
if (window.innerWidth < 768) {
setConfig({ itemPerPage: 4, bestItemPerPage: 1 });
} else if (window.innerWidth < 1025) {
setConfig({ itemPerPage: 6, bestItemPerPage: 2 });
} else {
setConfig({ itemPerPage: 10, bestItemPerPage: 4 });
}
};
handleResize(); // 최초 실행
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return config;
}이렇게 '디바이스에 따라서' 페이지의 최대값을 반환하는 훅을 만들어볼 수 있겠어요 !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(심화/더 나아가서) 더 나아가서 useDevice를 만들어볼 수도 있겠네요 !
리액트에서 현재 어떤 디바이스인지 알 수 있는 훅도 만들어볼 수 있을 것 같아요:
// hooks/useDevice.ts
import { useEffect, useState } from "react";
type DeviceType = "mobile" | "tablet" | "desktop";
const getDeviceType = (width: number): DeviceType => {
if (width < 768) return "mobile";
if (width < 1024) return "tablet";
return "desktop";
};
export default function useDevice(): DeviceType {
const [device, setDevice] = useState<DeviceType>(getDeviceType(window.innerWidth));
useEffect(() => {
const handleResize = () => {
setDevice(getDeviceType(window.innerWidth));
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return device;
}그리고 제안한 코드를 조금 수정해볼 수 있겠어요:
// hooks/useResponsivePagination.ts
import useDevice from "./useDevice";
export default function useResponsivePagination() {
const device = useDevice();
const config = {
mobile: { itemPerPage: 4, bestItemPerPage: 1 },
tablet: { itemPerPage: 6, bestItemPerPage: 2 },
desktop: { itemPerPage: 10, bestItemPerPage: 4 },
};
return config[device];
}이렇게 하면 useDevice의 재사용성도 더 좋겠네요. 😉😉
| fetch( | ||
| `https://panda-market-api.vercel.app/products?limit=${itemsPerPage}&page=${currentPage}`, | ||
| { method: "GET" } | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
baseUrl을 설정하면 더욱 유지보수가 좋게 코드를 관리할 수 있겠군요 !
| fetch( | |
| `https://panda-market-api.vercel.app/products?limit=${itemsPerPage}&page=${currentPage}`, | |
| { method: "GET" } | |
| ) | |
| fetch( | |
| `${baseUrl}/products?limit=${itemsPerPage}&page=${currentPage}`, | |
| { method: "GET" } | |
| ) |
위와 같은 설계가 된다면 더욱 좋을 것 같아요 !
상수로 관리할 수도 있고, 환경 변수로도 관리할 수 있을거예요.
환경 변수(Environment Variable):
process.env에 내장되며 앱이 실행될 때 적용할 수 있는 값입니다!
상수보다는 환경 변수로 관리하는게 일반적입니다 !
다음과 같이 적용할 수 있습니다:
// .env.development
REACT_APP_BASE_URL="http://localhost:3000"
// .env.production
REACT_APP_BASE_URL="http://myapi.com"
// 사용시
<a href={`${process.env.REACT_APP_BASE_URL}/myroute`}>URL</a>
왜 환경 변수에 저장해야 하나요?
개발(dev), 테스트(test), 실제 사용(prod) 등 다양한 환경에서 앱을 운영하게 되는 경우, 각 환경에 따라 다른 base URL을 사용해야 할 수 있습니다. 만약 코드 내에 하드코딩되어 있다면, 각 환경에 맞춰 앱을 배포할 때마다 코드를 변경해야 하며, 이는 매우 번거로운 작업이 됩니다. 하지만, 환경 변수를 .env.production, .env.development, .env.test와 같이 설정해두었다면, 코드에서는 단지 다음과 같이 적용하기만 하면 됩니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(응용) axios를 사용하는 것도 좋은 방법이예요!
어떻게 세팅하면 될까? 🤔
instance를 만들어서 export를 하고 사용해보는 것 정도로 시도해보면 좋을 것 같아요. axios-instance 파일을 만들어서 instance를 생성하고 export한 후 사용해보는건 어떨까요?
다음과 같이 만들어볼 수 있어요:
const baseURL = process.env.NEXT_PUBLIC_LINKBRARY_BaseURL;
const instance = axios.create({
baseURL: baseURL,
headers: {
'Content-Type': 'application/json',
},
});
export default instance인가에 필요한 accessToken을 localStorage가 있다면 axios의 인터셉터를 활용할 수 있습니다 !
인터셉터는 혼자 해결해보시는 것을 권장드립니다. 혹시 모르시겠으면 다음 위클리 미션에 질문해주세요. 😊
사용 방법 🚀
사용 방법은 정말 간단해요. 다음과 같이 사용할 수 있습니다:
instance.get(`/user/${userId}`)딱 보니. 마이그레이션도 정말 쉽게 할 수 있겠죠? 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
또한 보통 api 함수들은 따로 파일을 분류하여 사용하는게 일반적입니다 ! 😊
프로젝트에서는 API 호출 로직을 별도의 파일로 분리하여 관리하는 것이 일반적이예요. 이렇게 하면 컴포넌트, 페이지, 훅 등 어디든 사용될 수 있겠죠?😊
코드의 재사용성을 높이고, 유지보수가 쉬워질 수 있습니다 ! API 함수를 모듈화하여 사용하면 코드가 더 깔끔하고 읽기 쉬워집니다. 다음은 프로젝트의 디렉토리 구조와 API 함수 예제입니다:
// src/services/apis/production.api.js (예시입니다 !)
export const getProductions = async () => {
try {
const { data } = await axios.get('/productions');
return data;
} catch(error) {
throw error;
}
};| header { | ||
| padding: 0 24px; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
global.css는 모든 페이지에 스타일이 적용되는 것으로 보여요 !
그런데 header 태그는 한 문서에 여러개 작성될 수 있어요.
따라서 태그 선택자보다는 클래스 선택자로 작성해볼 수 있을거예요.
또한, 글로벌로 만들지 않고 필요한 헤더 컴포넌트에서 import 시키는 방법도 지역적으로 관리하기 용이할 수 있습니다 😉
|
수고하셨습니다 원탁님 ! 기초 프로젝트 시작인데 기초프로젝트에서는 어떤 모습을 보여주실지 기대가 됩니다 😊😊 |
요구사항
기본
체크리스트 [기본]
중고마켓
중고마켓 페이지 주소는 “/items” 입니다.
페이지 주소가 “/items” 일때 상단네비게이션바의 '중고마켓' 버튼의 색상은 “3692FF”입니다.
상단 네비게이션 바는 이전 미션에서 구현한 랜딩 페이지와 동일한 스타일로 만들어 주세요.
상품 데이터 정보는 https://panda-market-api.vercel.app/docs/#/ 에 명세된 GET 메소드 “/products” 를 사용해주세요.
'상품 등록하기' 버튼을 누르면 “/additem” 로 이동합니다. ( 빈 페이지 )
전체 상품에서 드롭 다운으로 “최신 순” 또는 “좋아요 순”을 선택해서 정렬을 할 수 있습니다.
중고마켓 반응형
베스트 상품
Desktop : 4개 보이기
Tablet : 2개 보이기
Mobile : 1개 보이기
전체 상품
Desktop : 12개 보이기
Tablet : 6개 보이기
Mobile : 4개 보이기
심화
페이지 네이션 기능을 구현합니다.
주요 변경사항
스크린샷
멘토에게
페이지네이션은 혼자 못해서 검색과 ai를 활용해 하였습니다