Django DB에서 데이터를 프론트엔드로 가져오기
fetch
JavaScript에서 제공하는 내장함수
네트워크 요청을 통해 서버로부터 데이터를 가져오거나 서버에 데이터를 전송하는데 사용
주로 API로부터 JSON 데이터나 HTML 리소스를 가져올 때 많이 사용
django-cors-headers
: fetch할 수 있는 사람을 지정
1) 터미널에서 설치: poetry add django-cors-headers
2) config > settings.py 의 THIRD_PARTY_APPS 에 'corsheaders' 추가
3) config > settings.py 의 MIDDLEWARE 에 'corsheaders.middleware.CorsMiddleware' 추가
4) config > settings.py 에 CORS_ALLOWED_ORIGINS = ["http://localhost:3000"] 추가
5) Home.tsx와 Room.tsx를 수정
: 예전 방식. 더 좋은 방법이 있다
TanStack Query(구 React Query)
: 서버에서 데이터를 가져오고 캐싱, 동기화, 업데이트 상태를 관리하기 위한 도구(data fetching을 효율적으로 할 수 있도록)
- API로부터 fetch한 모든 데이터를 캐싱한다(현재 페이지를 떠났다가 다시 돌아와도 이전에 fetch한 데이터를 기억)
- REST API만 사용하는 경우 TanStack Query, SWR이 적합
- GraphQL을 사용하면 Apollo Client나 Relay가 더 나은 선택일 수 있다
1) 터미널에서 설치: npm i @tanstack/react-query
2) index.tsx에서 import
3) src > api.ts 파일 생성(fetching 코드를 하나의 파일로 모으기)
4) Home.tsx 파일에서 useQuery를 사용하여 코드 수정
- useQuery 훅 사용
- useQuery 훅을 사용하여 getRooms 함수를 통해 비동기적으로 방 데이터를 가져옴
- queryKey: "rooms"라는 키를 지정하여 React Query가 캐시에 저장
- queryFn: getRooms 함수를 호출하여 서버에서 데이터를 가져옴
- isLoading: 데이터가 로딩 중일 때 true / 완료되면 false
- data: getRooms 함수에서 반환된 방 데이터가 저장
- fetch한 데이터는 브라우저의 메모리에 남아있어서(캐시 만들어 보존) 다시 돌아오면 로딩 없이 바로 뜬다...!
- Home 컴포넌트 구조
- Grid: 방 목록을 격자 형태로 배치. 반응형 설정을 통해 화면 크기에 따라 컬럼의 개수를 조정
- isLoading 조건: isLoading이 true일 때 RoomSkeleton을 보여주어 데이터가 로딩 중임을 나타냄
- 데이터 렌더링: data 배열의 각 room 객체를 Room 컴포넌트로 렌더링
- imageURL, name, rating, city, country, price 등 Room 컴포넌트에 필요한 데이터를 전달
- 옵셔널 체이닝(?.)을 사용하여 첫 번째 사진이 존재하지 않을 경우에도 오류가 발생하지 않도록 함
- 렌더링 결과:
- 로딩 중일 때는 RoomSkeleton으로 스켈레톤 UI가 나타나고, 로딩이 완료되면 Room 컴포넌트 목록이 화면에 표시
Axios
: fetch를 하는 일종의 adapter
1) 터미널에서 설치: npm i axios
2) api.ts 파일 수정: 훨씬 간결해졌다!
Room Detail
: 이미지를 클릭하면 세부정보를 표시하는 url로 이동
1) Room.tsx 에서 세부정보 페이지 링크 만들기: pk 설정
2) src > components > routes 에서 RoomDetail.tsx 파일 생성
3) src > router.tsx 에서 new childeren 추가
4) src > api.ts 에서 fetch 함수 만들기
Devtools and Query Keys
React DevTools
: React 컴포넌트 구조와 상태관리, 렌더링을 시각적으로 확인하여 디버그에 도움(Chrome, Firefox 확장 프로그램)
1) 터미널에서 설치: npm i @tanstack/react-query-devtools
2) src > components > Root.tsx 에서 import
3) src > components > routes > Roomdetail.tsx 코드 수정
Photos Grid
: RoomDetail 페이지 만들기
1) src > types.d.ts 파일 생성하여 데이터 타입 지정 & RoomDetail.tsx에서 <IRoomDetail> 추가
2) src > components > routes > RoomDetail.tsx 파일
Reviews
src > api.ts에서 getRoomReviews 함수 추가
src > components > routes > RoomDetail.tsx 에서도 useQuery로 추가
Avatar Component
- 사용자가 업로드한 프로필 이미지를 src 속성에 전달하여 표시
- 사용자 업로드 이미지가 없거나, 이미지에 문제가 있으면 사용자 이름으로 아바타를 만들 수 있다
- 뱃지 추가(색깔 등) 등 다양한 기능
import { useQuery } from "@tanstack/react-query";
import { useParams } from "react-router-dom";
import { getRoom, getRoomReviews } from "../../api";
import { IRoomDetail, IReview } from "../../types";
import { Box, Heading, Image, VStack, Text, HStack, Button, Skeleton, SkeletonText, Grid, GridItem, Avatar, AvatarBadge } from "@chakra-ui/react";
import { FaStar } from "react-icons/fa"
export default function RoomDetail() {
const { roomPk } = useParams();
// const { isLoading, data} = useQuery([`room:${roomPk}`], getRoom)
// // const { isLoading, data } = useQuery({queryKey: [`room:${roomPk}`], queryFn: () => getRoom,});
// const { isLoading, data } = useQuery({queryKey: [`rooms`, roomPk], queryFn: () => getRoom(roomPk)});
const { isLoading, data } = useQuery<IRoomDetail>({queryKey: ['rooms', roomPk], queryFn: getRoom // getRoom 함수를 직접 전달
});
const {isLoading:isReviewsLoading, data:reviewsData} = useQuery<IReview[]>({queryKey: ['rooms', roomPk, 'reviews'], queryFn: getRoomReviews});
return <Box mt={10} px={{base:10, lg:40}} >
<Skeleton height={"39.89px"} width={"25%"} isLoaded={!isLoading}>
<Heading>{data?.name}</Heading>
</Skeleton>
<Grid mt={8} rounded="xl" overflow={"hidden"} gap={"2"} height={"60vh"} templateRows={"1fr 1fr"} templateColumns={"repeat(4, 1fr)"}>
{[0,1,2,3,4].map((index) => (<GridItem colSpan={index===0? 2: 1} rowSpan={index===0? 2: 1} overflow={"hidden"} key={index}>
<Skeleton isLoaded={!isLoading} h="100%" w="100%">
<Image objectFit={"cover"} w="100%" h="100%" src={data?.photos[index].file} />
</Skeleton>
</GridItem>))}
</Grid>
<HStack width={"100%"} justifyContent={"space-between"} mt={10}>
<VStack alignItems={"flex-start"}>
<Skeleton isLoaded={!isLoading} height={"30px"}>
<Heading fontSize={"2xl"}>House hosted by {data?.owner.username}</Heading>
</Skeleton>
<Skeleton isLoaded={!isLoading} height={"30px"}>
<HStack justifyContent={"flex-start"} w="100%">
<Text>{data?.toilets} toilet{data?.toilets===1? "":"s"}</Text>
<Text>/</Text>
<Text>{data?.rooms} room{data?.toilets===1? "":"s"}</Text>
</HStack>
</Skeleton>
</VStack>
<Avatar name={data?.owner.username} size={"lg"} src={data?.owner.avatar}> {/* 이미지가 없는 경우 이름을 사용 */}
<AvatarBadge boxSize="1.25em" bg='red.500' />
</Avatar>
</HStack>
<Box mt={10}>
<Heading fontSize={"2xl"}>
<HStack>
<FaStar /> <Text>{data?.rating} / </Text>
<Text>{reviewsData?.length} review{reviewsData?.length===1? "":"s"}</Text>
</HStack>
</Heading>
</Box>
</Box>;
}
'airbnb clone coding' 카테고리의 다른 글
[Django/React] 20. AUTHENTICATION: LogOut (0) | 2024.11.22 |
---|---|
[Django/React] 20. AUTHENTICATION: useUser / Credentials (0) | 2024.11.18 |
[Django] 18. CHAKRA UI: Modal, Component, Grid, Responsive, Dark Mode, Skeleton, (0) | 2024.11.08 |
[React] 17. FRONT-END SETUP: Router, Not Found Page (1) | 2024.11.02 |
[Django] 16. API TESTING (0) | 2024.09.01 |