-
Notifications
You must be signed in to change notification settings - Fork 26
[김현화] sprint5 #112
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
[김현화] sprint5 #112
The head ref may contain hidden characters: "React-\uAE40\uD604\uD654-sprint5"
Conversation
|
스프리트 미션 하시느라 수고 많으셨어요. |
| const [isLogin, setIsLogin] = useState(true); | ||
| return ( | ||
| <> | ||
| <Header isLogin={isLogin} /> |
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.
오호 나중을 대비해서 일단 isLogin을 만들어둔건가 보군요? 😉
Header가 isLogin(혹은 user)을 받아두고 있으면 나중이 인증 인가 할 때 편하겠네요 👍
| import axios from "axios"; | ||
|
|
||
| //베스트 상품 | ||
| const BASE_URL = "https://panda-market-api.vercel.app/products?page="; |
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.
base URL은 환경 변수에 저장하시는게 좋습니다!
환경 변수(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와 같이 설정해두었다면, 코드에서는 단지 다음과 같이 적용하기만 하면 됩니다.
const apiUrl = `${process.env.REACT_APP_BASE_URL}/api`;
이러한 방식으로 환경 변수를 사용하면, 배포 환경에 따라 쉽게 URL을 변경할 수 있으며, 코드의 가독성과 유지보수성도 개선됩니다.
실제 코드 응용과 관련해서는 다음 한글 아티클을 참고해보세요! => 보러가기
| const response = await axios.get( | ||
| `${BASE_URL}1&pageSize=${pageSize}&orderBy=favorite` | ||
| ); |
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.
쿼리는 URLSearchParams로 손쉽게 사용할 수 있어요 !
const params = new URLSearchParams({ pageSize });
const { data } = await instance.get(`${BASE_URL}`, { params });axios를 사용하실 경우 URLSearchParams와 함께 객체로 손쉽게 핸들링할 수 있습니다 !
객체로 구성할 수 있어 가독성이 좋고, URL 인코딩을 자동으로 처리하여 특수 문자나 공백이 포함된 값에서도 안전하게 동작합니다 !
URLSearchParams:
URLSearchParams인터페이스는 URL의 쿼리 문자열을 대상으로 작업할 수 있는 유틸리티 메서드를 정의합니다.
쿼리를 생성하실 때에 참고해서 사용해보세요 😊
| const response = await axios.get( | ||
| `${BASE_URL}${page}&pageSize=${pageSize}&orderBy=${orderBy}` | ||
| ); |
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를 직접 사용하고 계시군요 !
axios를 직접 사용하는 것도 현재 기능상 문제가 없으나 이렇게 되면 BASE_URL을 통신 할 때마다 계속해서 불러와야되겠어요 !
그럼 어떻게? 🤔
instance를 만들어서 export를 하고 사용해보실 수 있어요. axios-instance 파일(다른 이름도 됩니다 !)을 만들어서 instance를 생성하고 export한 후 사용해보는건 어떨까요?
다음과 같이 만들어볼 수 있어요:
const baseURL = process.env.BASE_URL; // 왠만하면 `path`를 제외한 BASE URL로 저장하면 좋아요 ! (= `https://panda-market-api.vercel.app`)
const instance = axios.create({
baseURL: baseURL,
headers: {
'Content-Type': 'application/json',
},
});
export default instance그리고 다음과 같이 사용해볼 수 있어요 😉
| const response = await axios.get( | |
| `${BASE_URL}${page}&pageSize=${pageSize}&orderBy=${orderBy}` | |
| ); | |
| import instance from '...'; | |
| // ... | |
| const response = await instance.get( | |
| `/products?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}` | |
| ); |
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.
(선택/제안) 무언가 공통적으로 사용될 것 같은 컴포넌트군요 ?
여러 컴포넌트에서 사용될 것 같다면 src/components/common/Container.jsx 혹은src/components/common/Container/index.jsx와 같이 디렉토리를 구분해도 될 것 같군요 🤔😉
라고 적고 확인해보니 사용을 안하고 있었네요 😅
파일 구조 컨벤션에 따라 다를 수 있으나 두 곳 이상에서 사용될 것 같으면 common으로 영역을 구분한다 ! 정도로만 받아들여주시면 좋겠습니다 !
|
|
||
| function Pagination({ onPageLoad, totalCount, itemListLength }) { | ||
|
|
||
| //페이지네이션 최대 생성 갯수 | ||
| const LIST_MAX = 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.
LIST_MAX의 경우 상수로 보입니다 !
| function Pagination({ onPageLoad, totalCount, itemListLength }) { | |
| //페이지네이션 최대 생성 갯수 | |
| const LIST_MAX = 5; | |
| //페이지네이션 최대 생성 갯수 | |
| const LIST_MAX = 5; | |
| function Pagination({ onPageLoad, totalCount, itemListLength }) { |
해당 값은 리렌더링 시 불필요한 선언이 될 수 있으므로 컴포넌트 바깥에서 선언해볼 수 있습니다 !
이렇게 하다보면 컴포넌트 안에는 상태나 props를 참조하는 것들만 남게 되겠죠? 😉
| const paginationArr = (startPage, maxPages) => { | ||
| const pages = []; | ||
| for (let i = 0; i < maxPages; i++) { | ||
| pages.push(startPage + i); | ||
| } | ||
| return pages; | ||
| }; |
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.
해당 함수도 컴포넌트 내부의 자원을 사용하는 것 같아 보이지 않네요 !
바깥에 선언해서 사용하셔도 될 것 같아요 😊
| // 페이지네이션 렌더링 | ||
| const renderPages = () => { | ||
| return pages.map((number) => ( | ||
| <li key={number}> | ||
| <button | ||
| className={`${wrap.button} ${ | ||
| currentPage === number ? wrap.active : "" | ||
| }`} | ||
| onClick={() => handlePageClick(number)} | ||
| > | ||
| {number} | ||
| </button> | ||
| </li> | ||
| )); | ||
| }; |
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.
오호 해당 함수도 컴포넌트 바깥에 컴포넌트로 선언해볼 수 있겠네요 !
| // 페이지네이션 렌더링 | |
| const renderPages = () => { | |
| return pages.map((number) => ( | |
| <li key={number}> | |
| <button | |
| className={`${wrap.button} ${ | |
| currentPage === number ? wrap.active : "" | |
| }`} | |
| onClick={() => handlePageClick(number)} | |
| > | |
| {number} | |
| </button> | |
| </li> | |
| )); | |
| }; | |
| // 페이지네이션 렌더링 | |
| const PageNumbers = ({ onClick }) => { | |
| return pages.map((number) => ( | |
| <li key={number}> | |
| <button | |
| className={`${wrap.button} ${ | |
| currentPage === number ? wrap.active : "" | |
| }`} | |
| onClick={onClick} | |
| > | |
| {number} | |
| </button> | |
| </li> | |
| )); | |
| }; |
그리고 onClick을 매개변수로 받아볼 수 있을 것 같아요 !
(이어서..)
| <button className={wrap.button} onClick={handlePrevClick}> | ||
| <img src={leftArrowIcon} alt="이전페이지" /> | ||
| </button> | ||
| <ul>{renderPages()}</ul> |
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.
(이어서) 다음과 같이 작성해볼 수 있습니다 !
| <ul>{renderPages()}</ul> | |
| <ul> | |
| <PageNumbers onClick={handlePageClick} /> | |
| </ul> |
| useEffect(() => { | ||
| handleListLoad({ page, pageSize: itemListLength, orderBy }); | ||
| }, [page, orderBy, isTABLET, isMOBILE]); | ||
|
|
||
| //선택한 페이지 리스트 불러오기 | ||
| const handlePageLoad = (pageNumber) => { | ||
| setPage(pageNumber); | ||
| handleListLoad({ page: pageNumber, pageSize: itemListLength, orderBy }); | ||
| }; |
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에서 page가 변경될 때 통신을 하고 있습니다 !
| useEffect(() => { | |
| handleListLoad({ page, pageSize: itemListLength, orderBy }); | |
| }, [page, orderBy, isTABLET, isMOBILE]); | |
| //선택한 페이지 리스트 불러오기 | |
| const handlePageLoad = (pageNumber) => { | |
| setPage(pageNumber); | |
| handleListLoad({ page: pageNumber, pageSize: itemListLength, orderBy }); | |
| }; | |
| useEffect(() => { | |
| handleListLoad({ page, pageSize: itemListLength, orderBy }); | |
| }, [page, orderBy, isTABLET, isMOBILE]); | |
| //선택한 페이지 리스트 불러오기 | |
| const handlePageLoad = (pageNumber) => { | |
| setPage(pageNumber); | |
| }; |
따라서 handlePageLoad에서 handleListLoad는 제거해주셔도 될 것 같아요 😊
만약 handleListLoad가 있다면
- 다른 페이지를 클릭한다.
handlePageLoad에 의해handleListLoad이 호출된다. (통신)setPage에 의해page가 변경된다.page를 의존하고 있는useEffect가 실행되며handleListLoad가 실행 된다. (통신)
이렇게 중복 통신이 되는 원인으로 보입니다 !
|
크으 ~ 수고하셨습니다 현화님 !! 순수하게 직접 탐구하시면서 작성하신 것으로 보여서 정말 뿌듯하게 지켜봤습니다 ! 궁금하신 점 있으시면 DM 주세요 현화님 ! (타입스크립트도 화이팅 ! 👏) |

요구사항
배포 : https://poetic-fenglisu-a8fdd0.netlify.app/items
기본
중고마켓
중고마켓 반응형
베스트 상품
전체 상품
심화
주요 변경사항
스크린샷
멘토에게
배포 후 리퀘스트를 확인해보니 200코드와 304코드가 함께 오는데, 무엇 때문인지 이유를 모르겠습니다.