[노현지] Sprint6#66
Hidden character warning
Conversation
|
스프리트 미션 하시느라 수고 많으셨어요. |
스프린트 미션 7 전에 컴포넌트 분리 시도해보겠습니다.크으 좋습니다 ! 리팩토링 해보시면 좋은 경험이 되실거예요 😊 |
| function App() { | ||
| const location = useLocation(); | ||
|
|
||
| return ( | ||
| <> | ||
| {!["/signin", "/signup"].includes(location.pathname) && ( | ||
| <Header className={styles.nav} /> | ||
| )} | ||
| <div className={styles.body}> | ||
| <Outlet /> | ||
| </div> | ||
| {["/"].includes(location.pathname) && ( | ||
| <Footer className={styles.footer} /> | ||
| )} | ||
| </> | ||
| ); | ||
| } |
There was a problem hiding this comment.
(제안/선택) 별칭을 사용하면 가독성을 향상시킬 수 있습니다 😊😊
| function App() { | |
| const location = useLocation(); | |
| return ( | |
| <> | |
| {!["/signin", "/signup"].includes(location.pathname) && ( | |
| <Header className={styles.nav} /> | |
| )} | |
| <div className={styles.body}> | |
| <Outlet /> | |
| </div> | |
| {["/"].includes(location.pathname) && ( | |
| <Footer className={styles.footer} /> | |
| )} | |
| </> | |
| ); | |
| } | |
| function App() { | |
| const location = useLocation(); | |
| const isAuthPage = ["/signin", "/signup"].includes(location.pathname); | |
| const isHomePage = location.pathname === "/"; | |
| return ( | |
| <> | |
| {!isAuthPage && <Header className={styles.nav} />} | |
| <div className={styles.body}> | |
| <Outlet /> | |
| </div> | |
| {isHomePage && <Footer className={styles.footer} />} | |
| </> | |
| ); | |
| } |
인라인 조건문을 변수로 추출하면 코드의 의도가 명확해지고 유지보수가 쉬워집니다! 이렇게 하면 가독성이 좋아지고, 새로운 경로가 추가될 때도 쉽게 관리할 수 있어요. 🚀
| function useWindowSize() { | ||
| const [windowSize, setWindowSize] = useState({ | ||
| width: window.innerWidth, | ||
| height: window.innerHeight, | ||
| }); | ||
|
|
||
| useEffect(() => { | ||
| const handleResize = debounce(() => { | ||
| setWindowSize({ | ||
| width: window.innerWidth, | ||
| height: window.innerHeight, | ||
| }); | ||
| }, 300); | ||
|
|
||
| window.addEventListener("resize", handleResize); | ||
| return () => { | ||
| window.removeEventListener("resize", handleResize); | ||
| }; | ||
| }, []); | ||
|
|
||
| return windowSize; | ||
| } |
There was a problem hiding this comment.
300이란 값을 파라메터로 받아볼 수 있어요 !:
크으 ~ 커스텀 훅으로 만드셨군요 ! 👍👍👍
| function useWindowSize() { | |
| const [windowSize, setWindowSize] = useState({ | |
| width: window.innerWidth, | |
| height: window.innerHeight, | |
| }); | |
| useEffect(() => { | |
| const handleResize = debounce(() => { | |
| setWindowSize({ | |
| width: window.innerWidth, | |
| height: window.innerHeight, | |
| }); | |
| }, 300); | |
| window.addEventListener("resize", handleResize); | |
| return () => { | |
| window.removeEventListener("resize", handleResize); | |
| }; | |
| }, []); | |
| return windowSize; | |
| } | |
| function useWindowSize(delay = 300) { | |
| const [windowSize, setWindowSize] = useState({ | |
| width: window.innerWidth, | |
| height: window.innerHeight, | |
| }); | |
| useEffect(() => { | |
| const handleResize = debounce(() => { | |
| setWindowSize({ | |
| width: window.innerWidth, | |
| height: window.innerHeight, | |
| }); | |
| }, delay); | |
| window.addEventListener("resize", handleResize); | |
| return () => { | |
| window.removeEventListener("resize", handleResize); | |
| }; | |
| }, []); | |
| return windowSize; | |
| } |
| import { useState, useEffect } from "react"; | ||
| import debounce from "lodash.debounce"; | ||
|
|
||
| function useWindowSize() { |
There was a problem hiding this comment.
(제안/심화/의견) debounce도 useDebounce라는 커스텀 훅으로 만들어볼 수도 있을 것 같아요 😊
| const handleLoad = async (query) => { | ||
| try { | ||
| const { list, totalCount } = await getItems(query); | ||
| setItems(list); | ||
| setTotalItemCount(totalCount); | ||
| } catch (error) { | ||
| return; | ||
| } | ||
| }; | ||
|
|
||
| useEffect(() => { | ||
| handleLoad({ page, pageSize, order }); | ||
| }, [page, pageSize, order]); |
There was a problem hiding this comment.
handleLoad를 useEffect 안에 선언하는건 어떨까요?
| const handleLoad = async (query) => { | |
| try { | |
| const { list, totalCount } = await getItems(query); | |
| setItems(list); | |
| setTotalItemCount(totalCount); | |
| } catch (error) { | |
| return; | |
| } | |
| }; | |
| useEffect(() => { | |
| handleLoad({ page, pageSize, order }); | |
| }, [page, pageSize, order]); | |
| useEffect(() => { | |
| const handleLoad = async () => { | |
| try { | |
| const { list, totalCount } = await getItems({ page, pageSize, order }); | |
| setItems(list); | |
| setTotalItemCount(totalCount); | |
| } catch (error) { | |
| console.error("아이템을 불러오는 중 오류 발생:", error); | |
| } | |
| }; | |
| handleLoad(); | |
| }, [page, pageSize, order]); | |
handleLoad는 useEffect에서만 사용되는 함수이므로, 굳이 컴포넌트 바깥에 선언할 필요가 없어요 ! useEffect 안에서 선언하면 불필요한 외부 접근을 방지하고, 코드 응집도가 높아져 관리가 쉬워집니다. 😊
| } catch (error) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
오류가 발생했음을 알려주는 건 어떨까요? 😊
| } catch (error) { | |
| return; | |
| } | |
| } catch (error) { | |
| alert(error.message); | |
| console.error('ERROR: ', error); | |
| } |
|
|
||
| function AllItems() { | ||
| const [order, setOrder] = useState("recent"); | ||
| const [page, setPage] = useState(1); | ||
| const [pageSize, setPageSize] = useState(10); | ||
| const [isOpen, setIsOpen] = useState(false); | ||
| const [totalItemCount, setTotalItemCount] = useState(0); | ||
| const [pageBound, setPageBound] = useState(0); | ||
| const [items, setItems] = useState([]); | ||
| const MaxPageBound = Math.floor(totalItemCount / pageSize / 5); | ||
| const pageArr = [1, 2, 3, 4, 5]; |
There was a problem hiding this comment.
pageArr 컴포넌트의 자원을 사용하지 않기에 상수로 표현할 수 있으며 컴포넌트 바깥에 선언하실 수 있습니다 !
| function AllItems() { | |
| const [order, setOrder] = useState("recent"); | |
| const [page, setPage] = useState(1); | |
| const [pageSize, setPageSize] = useState(10); | |
| const [isOpen, setIsOpen] = useState(false); | |
| const [totalItemCount, setTotalItemCount] = useState(0); | |
| const [pageBound, setPageBound] = useState(0); | |
| const [items, setItems] = useState([]); | |
| const MaxPageBound = Math.floor(totalItemCount / pageSize / 5); | |
| const pageArr = [1, 2, 3, 4, 5]; | |
| const PAGE_ARRAY = [1, 2, 3, 4, 5]; | |
| function AllItems() { | |
| const [order, setOrder] = useState("recent"); | |
| const [page, setPage] = useState(1); | |
| const [pageSize, setPageSize] = useState(10); | |
| const [isOpen, setIsOpen] = useState(false); | |
| const [totalItemCount, setTotalItemCount] = useState(0); | |
| const [pageBound, setPageBound] = useState(0); | |
| const [items, setItems] = useState([]); | |
| const MaxPageBound = Math.floor(totalItemCount / pageSize / 5); |
pageArr는 컴포넌트 내부 상태, props와 연관이 없으므로 컴포넌트 바깥에 선언해볼 수 있어요. 이렇게 하시면 컴포넌트의 내용들은 컴포넌트의 자원과 관련된 로직들로만 구성되며, 리렌더링에 의한 불필요한 재선언을 방지할 수 있습니다 😊
| if (width > 1200) { | ||
| newPageSize = 10; // PC | ||
| } else if (width > 768) { |
There was a problem hiding this comment.
(제안) 1200, 768과 같은 숫자가 많이 보이는군요 !
| if (width > 1200) { | |
| newPageSize = 10; // PC | |
| } else if (width > 768) { | |
| if (width > BREAKPOINTS.DESKTOP) { | |
| newPageSize = 10; // PC | |
| } else if (width > BREAKPOINTS.TABLET) { |
앞으로도 재사용이 많을 것으로 예상되어 유지보수성과 가독성을 위해 상수로 정의해두면 어떨지 제안드려봅니다 !
| <div className={styles.title}>전체 상품</div> | ||
| <div className={styles.menu}> | ||
| <form> | ||
| <img src={SearchIcon} className={styles.searchIcon} alt="검색" /> |
There was a problem hiding this comment.
단순 장식용으로 있을 경우 ""로 나타낼 수 있습니다 !
| <img src={SearchIcon} className={styles.searchIcon} alt="검색" /> | |
| <img src={SearchIcon} className={styles.searchIcon} alt="" /> |
장식 이미지는 페이지 콘텐츠에 정보를 추가하지 않습니다. 예를 들어, 이미지에서 제공하는 정보는 인접한 텍스트를 사용하여 이미 제공될 수도 있고, 웹 사이트를 시각적으로 더욱 매력적으로 만들기 위해 이미지가 포함될 수도 있습니다.
이러한 경우 스크린 리더와 같은 보조 기술에서 무시할 수 있도록 null(빈) alt텍스트를 제공해야 합니다( ). alt=""이러한 유형의 이미지에 대한 텍스트 값은 화면 판독기 출력에 청각적 혼란을 추가하거나 주제가 인접한 텍스트의 주제와 다른 경우 사용자의 주의를 산만하게 할 수 있습니다. 속성 을 생략하는 alt것도 옵션이 아닙니다. 속성이 제공되지 않으면 일부 화면 판독기가 이미지의 파일 이름을 대신 알려주기 때문입니다.
|
현지님 ! 수고하셨습니다 😊 미션 수행하시느라 정말 고생 많으셨습니다 현지님 😊😊 |
요구사항
기본
상품 등록
심화
상품 등록
주요 변경사항
멘토에게