Skip to content

Commit

Permalink
Feat: 메인페이지 api 연결 (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
seobbang authored Sep 29, 2024
1 parent e9c0988 commit b95985e
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 94 deletions.
48 changes: 48 additions & 0 deletions src/apis/public/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 검색 관련 공공 데이터 API

import { AreaCodeItem, PlaceBasedAreaItem } from '@/types/main';
import { Response } from '@/types/public';

import { publicDataClient } from '..';

interface placeBasedAreaParams {
region: string;
}

export const getAreaCode = async () => {
const params = `MobileApp=UNITRIP&_type=json&numOfRows=20&MobileOS=ETC&serviceKey=${import.meta.env.VITE_PUBLIC_DATA_SERVICE_KEY}`;

const {
data: {
response: {
body: { items },
},
},
} = await publicDataClient.get<Response<AreaCodeItem[]>>(
`/areaCode1?${params}`,
);
return items;
};

export const getPlaceBasedArea = async (paramsInfo: placeBasedAreaParams) => {
const areaItem = await getAreaCode();

const areaCode =
areaItem === ''
? '1'
: areaItem.item.find(({ name }) => paramsInfo.region.startsWith(name))
?.code;

const params = `MobileApp=UNITRIP&_type=json&arrange=Q&contentTypeId=12&areaCode=${areaCode || '1'}&MobileOS=ETC&serviceKey=${import.meta.env.VITE_PUBLIC_DATA_SERVICE_KEY}`;

const {
data: {
response: {
body: { items },
},
},
} = await publicDataClient.get<Response<PlaceBasedAreaItem[]>>(
`/areaBasedList1?${params}`,
);
return items;
};
4 changes: 2 additions & 2 deletions src/apis/public/search.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// 검색 관련 공공 데이터 API

import { Response } from '@/types/public';
import { SearchWord } from '@/types/search';
import { SearchItem } from '@/types/search';

import { publicDataClient } from '..';

Expand All @@ -26,7 +26,7 @@ export const getSearchKeyword = async (paramsInfo: searchKeywordParams) => {
body: { items },
},
},
} = await publicDataClient.get<Response<SearchWord>>(
} = await publicDataClient.get<Response<SearchItem[]>>(
`/searchKeyword1?${params}`,
);
return items;
Expand Down
22 changes: 12 additions & 10 deletions src/components/PlaceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ const cardContainerCss = (imgSrc: string, placeName: string) => css`
height: 16.8rem;
border-radius: 1.2rem;
background-image: url(${imgSrc});
background-size: cover;
background-position: center center;
background-color: ${placeName ? COLORS.gray4 : COLORS.gray2};
background-position: center center;
background-size: cover;
background-image: url(${imgSrc});
`;

const backgroundCss = css`
Expand All @@ -76,24 +76,26 @@ const backgroundCss = css`
height: 16.8rem;
border-radius: 1.2rem;
color: ${COLORS.white};
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.34) 100%
rgb(0 0 0 / 0%) 0%,
rgb(0 0 0 / 34%) 100%
);
color: ${COLORS.white};
`;

const titleCss = css`
margin: 9.4rem 0 0 1.6rem;
${FONTS.H3};
overflow: hidden;
width: calc(100% - 1.6rem);
margin: 9.4rem 0 0 1.6rem;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
${FONTS.H3};
`;

const addressCss = css`
Expand Down
29 changes: 29 additions & 0 deletions src/types/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export interface PlaceBasedAreaItem {
mapy: string;
mlevel: string;
cpyrhtDivCd: string;
firstimage: string;
firstimage2: string;
mapx: string;
booktour: string;
contentid: string;
cat3: string;
cat2: string;
modifiedtime: string;
sigungucode: string;
tel: string;
title: string;
zipcode: string;
addr1: string;
addr2: string;
areacode: string;
cat1: string;
contenttypeid: string;
createdtime: string;
}

export interface AreaCodeItem {
rnum: string;
code: string;
name: string;
}
17 changes: 16 additions & 1 deletion src/types/public.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
export interface Response<T> {
response: T;
response: {
header: {
resultCode: string;
resultMsg: string;
};
body: {
numOfRows: number;
pageNo: number;
totalCount: number;
items:
| {
item: T;
}
| '';
};
};
}
19 changes: 1 addition & 18 deletions src/types/search.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface SearchResItem {
export interface SearchItem {
cat2: string;
cat3: string;
tel: string;
Expand All @@ -20,20 +20,3 @@ export interface SearchResItem {
createdtime: string;
firstimage: string;
}

export interface SearchWord {
header: {
resultCode: string;
resultMsg: string;
};
body: {
numOfRows: number;
pageNo: number;
totalCount: number;
items:
| {
item: SearchResItem[];
}
| '';
};
}
58 changes: 35 additions & 23 deletions src/views/Main/components/NearbyTravel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@ import { css } from '@emotion/react';
import { useState } from 'react';
import { Link } from 'react-router-dom';

import { getPlaceBasedArea } from '@/apis/public/main';
import LoginModal from '@/components/LoginModal';
import { useAsyncEffect } from '@/hooks/use-async-effect';
import { COLORS, FONTS } from '@/styles/constants';
import { PlaceBasedAreaItem } from '@/types/main';

import { cardContainer, scrollContainer } from '../styles/main';
import TravelCard from './TravelCard';

interface NearbyTravelProps {
isLoggedIn: boolean;
region?: string; // prop 타입 수정
region?: string;
favoriteList?: number[];
}

const NearbyTravel = ({ isLoggedIn, region }: NearbyTravelProps) => {
const NearbyTravel = (props: NearbyTravelProps) => {
const { isLoggedIn, region, favoriteList } = props;
const [activateModal, setActivateModal] = useState(false);
const [placeList, setPlaceList] = useState<PlaceBasedAreaItem[]>([]);

const closeModal = () => {
setActivateModal(false);
Expand All @@ -24,33 +30,39 @@ const NearbyTravel = ({ isLoggedIn, region }: NearbyTravelProps) => {
setActivateModal(true);
};

useAsyncEffect(async () => {
if (!region) return;
const placeList = await getPlaceBasedArea({
region: region || '서울',
});
setPlaceList(placeList === '' ? [] : placeList.item);
}, [region]);

return (
<section css={container}>
<h2 css={title}>{isLoggedIn && region} 주변 갈 만한 여행지 🗺️</h2>
<h2 css={title}>
{isLoggedIn && (region || '서울')} 주변 갈 만한 여행지 🗺️
</h2>
{isLoggedIn ? (
<>
<div css={scrollContainer}>
<li css={cardContainer}>
<TravelCard
name="대전 오월드"
address="대전 중구 사정공원로 70"
/>
<TravelCard
name="대전 오월드"
address="대전 중구 사정공원로 70"
/>
<TravelCard
name="대전 오월드"
address="대전 중구 사정공원로 70"
/>
<TravelCard
name="대전 오월드"
address="대전 중구 사정공원로 70"
/>
</li>
<ul css={cardContainer}>
{placeList.map(
({ title, addr1, addr2, contentid, firstimage }) => (
<TravelCard
key={contentid}
contentid={contentid}
name={title}
address={`${addr1} ${addr2}`}
imgUrl={firstimage}
isHeart={!!favoriteList?.includes(Number(contentid))}
/>
),
)}
</ul>
</div>
<Link to="" css={link}>
{region} 여행지 둘러보기
<Link to="/map" css={link}>
{region || '서울'} 여행지 둘러보기
</Link>
</>
) : (
Expand Down
63 changes: 49 additions & 14 deletions src/views/Main/components/TravelCard.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,68 @@
import { css } from '@emotion/react';
import { Link } from 'react-router-dom';

import { HeartMonoIcon, PinLocationMonoIcon } from '@/assets/icon';
import { HeartFillMonoIcon, PinLocationMonoIcon } from '@/assets/icon';
import { COLORS, FONTS } from '@/styles/constants';

interface TravelCardProps {
contentid: string;
name: string;
address: string;
imgUrl: string;
isHeart: boolean;
}

const TravelCard = (props: TravelCardProps) => {
const { name, address } = props;
const { contentid, name, address, imgUrl, isHeart } = props;

return (
<ul css={card}>
<button css={heart} onClick={() => {}}>
<HeartMonoIcon />
</button>
<p css={nameCss}>{name}</p>
<div css={addressContainer}>
<PinLocationMonoIcon />
<address css={locationCss}>{address}</address>
<Link to={`/${contentid}`} css={card(imgUrl)}>
<div css={background}>
<div css={heart}>{isHeart && <HeartFillMonoIcon />}</div>
<p css={nameCss}>{name}</p>
<div css={addressContainer}>
<PinLocationMonoIcon />
<address css={locationCss}>{address}</address>
</div>
</div>
</ul>
</Link>
);
};

export default TravelCard;

const card = css`
const card = (imgUrl: string) => css`
position: relative;
height: 24.8rem;
border-radius: 1.2rem;
background-color: ${COLORS.gray4};
background-position: center center;
background-size: cover;
background-image: url(${imgUrl});
min-width: 23.2rem;
`;

const background = css`
display: flex;
flex-direction: column;
position: absolute;
top: 0;
left: 0;
width: 21.2rem;
height: 24.8rem;
padding: 1.6rem;
border-radius: 1.2rem;
background-color: ${COLORS.brand1};
background: linear-gradient(
180deg,
rgb(0 0 0 / 0%) 0%,
rgb(0 0 0 / 34%) 100%
);
min-width: 23.2rem;
`;

const heart = css`
Expand All @@ -50,11 +77,19 @@ const nameCss = css`
`;

const locationCss = css`
overflow: hidden;
color: ${COLORS.white};
white-space: nowrap;
text-overflow: ellipsis;
${FONTS.Small2};
`;

const addressContainer = css`
display: flex;
gap: 0.3rem;
align-items: center;
margin-top: 0.2rem;
`;
Loading

0 comments on commit b95985e

Please sign in to comment.