[김현화] sprint5#112
Hidden character warning
Conversation
|
스프리트 미션 하시느라 수고 많으셨어요. |
| const [isLogin, setIsLogin] = useState(true); | ||
| return ( | ||
| <> | ||
| <Header isLogin={isLogin} /> |
There was a problem hiding this comment.
오호 나중을 대비해서 일단 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.
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.
쿼리는 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.
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.
(선택/제안) 무언가 공통적으로 사용될 것 같은 컴포넌트군요 ?
여러 컴포넌트에서 사용될 것 같다면 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.
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.
해당 함수도 컴포넌트 내부의 자원을 사용하는 것 같아 보이지 않네요 !
바깥에 선언해서 사용하셔도 될 것 같아요 😊
| // 페이지네이션 렌더링 | ||
| 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.
오호 해당 함수도 컴포넌트 바깥에 컴포넌트로 선언해볼 수 있겠네요 !
| // 페이지네이션 렌더링 | |
| 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.
(이어서) 다음과 같이 작성해볼 수 있습니다 !
| <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.
이미 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코드가 함께 오는데, 무엇 때문인지 이유를 모르겠습니다.