Skip to content
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

[DETAIL] 메인페이지 주소 변경 UI 구현 #95

Merged
merged 18 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15,732 changes: 15,732 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"axios": "^0.27.2",
"http-proxy-middleware": "1",
hyejun0228 marked this conversation as resolved.
Show resolved Hide resolved
"jotai": "^1.11.0",
"qs": "^6.11.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.36.1",
"react-naver-maps": "^0.1.2",
"react-query": "^3.39.1",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
Expand Down Expand Up @@ -71,5 +73,5 @@
"stylelint-config-standard": "^26.0.0",
"stylelint-config-standard-scss": "^4.0.0"
},
"proxy": "https://nid.naver.com"
"proxy": "https://naveropenapi.apigw.ntruss.com"
Comment on lines -74 to +76
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네이버 로그인을 위한 개발용 프록시였는데, 바꿔야만 동작하나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네이버 클라우드 플렛폼에 있는 현재의 위치를 가져와 지도에 표시해주는 api를 사용해주기 위해서는 proxy 를 바꿔줄 필요가 있어 바꿔주게 됐습니다. 지금의 프록시로 바꿨을때 네이버 로그인이 작동되는지 확인이 안되는데 따로 확인할 방법이 있을까요? 코드상에서 유추해봐야하는건가요?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

환경변수를 적절히 넣은 다음 네이버 로그인을 시도해보면 되겠죠..?

}
25 changes: 25 additions & 0 deletions src/api/location/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import axios from 'axios';
import { NAVER_MAPS_CLIENT_ID, NAVER_MAP_CLOUD_SECRET_ID } from 'config/constants';

const getAddress = async (latitude: number, longitude: number): Promise<string> => {
const response = await axios.get('/map-reversegeocode/v2/gc', {
params: {
coords: `${longitude},${latitude}`,
sourcecrs: 'epsg:4326',
orders: 'roadaddr',
output: 'json',
},
headers: {
'X-NCP-APIGW-API-KEY-ID': NAVER_MAPS_CLIENT_ID,
'X-NCP-APIGW-API-KEY': NAVER_MAP_CLOUD_SECRET_ID,
},
});

const { data } = response;
if (data?.results?.length > 0) {
return `${data.results[0].region.area1.name} ${data.results[0].region.area2.name} ${data.results[0].region.area3.name} ${data.results[0].land.name} ${data.results[0].land.number1}`;
}
return '주소 정보 없음';
};

export default getAddress;
3 changes: 3 additions & 0 deletions src/assets/svg/home/arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/assets/svg/home/point.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/svg/home/search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const SERVER_LOGIN_REDIRECT_URL = checkEnvVar('REACT_APP_SERVER_LOGIN_RED

// 네이버 지도
export const NAVER_MAPS_CLIENT_ID = checkEnvVar('REACT_APP_NAVER_MAPS_CLIENT_ID');
export const NAVER_MAP_CLOUD_SECRET_ID = checkEnvVar('REACT_APP_NAVER_CLOUD_MAPS_CLIENT_SECRET_ID');

// 구글 OAuth
export const GOOGLE_CLIENT_ID = checkEnvVar('REACT_APP_GOOGLE_CLIENT_ID');
Expand Down
16 changes: 16 additions & 0 deletions src/pages/Home/Home.module.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
.home {
width: 100%;
}

.map {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}

.locationBox {
position: absolute;
top: 0;
left: 0;
z-index: 3;
}
37 changes: 37 additions & 0 deletions src/pages/Home/components/LocationInfo/LocationInfo.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@use "src/utils/styles/mediaQuery.scss" as media;

.map-container {
display: none;

@include media.media-breakpoint-up(mobile) {
position: absolute;
right: 24px;
top: 24px;
height: 57px;
border-radius: 100px;
border: none;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 20px;
font-weight: 500;
line-height: 24.96px;
color: #222222;
z-index: 2;
backdrop-filter: blur(2px);
background-color: #ffffff99;
box-shadow: 1px 1px 10px 1px #0000001a;
cursor: pointer;
}

&__text {
display: flex;
margin-left: 24px;
}

&__image {
display: flex;
margin-right: 24px;
margin-left: 10px;
}
}
24 changes: 24 additions & 0 deletions src/pages/Home/components/LocationInfo/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ReactComponent as Arrow } from 'assets/svg/home/arrow.svg';
import styles from './LocationInfo.module.scss';

interface LocationInfoProps {
address: string | null;
onClick: () => void;
}

export default function LocationInfo({ address, onClick }: LocationInfoProps): JSX.Element {
return (
<button
type="button"
className={styles['map-container']}
onClick={onClick}
>
{address ? (
<div className={styles['map-container__text']}>{address}</div>
) : (
<div className={styles['map-container__text']}>위치 정보를 가져오는 중...</div>
)}
<Arrow className={styles['map-container__image']} />
</button>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
.container {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
width: 617px;
height: 749px;
flex-direction: column;
border-radius: 15px;
backdrop-filter: blur(2px);
background-color: #ffffffcc;
z-index: 3;
}

.box {
display: flex;
flex-direction: column;
align-items: center;

&__title {
display: flex;
color: #ff7f23;
font-size: 28px;
font-weight: 700;
margin: 68px auto 12px;
}

&__subTitle {
display: flex;
font-size: 16px;
font-weight: 400;
}
}

.search {
display: flex;
align-items: baseline;
margin-left: 40px;

&__box {
display: flex;
width: 426px;
height: 46px;
border-radius: 20px;
background-color: white;
box-shadow: 5px 5px 15px 1px #0000001a;
border: none;
margin-top: 48px;
}

&__image {
display: flex;
margin: auto 20px;
}

&__text {
display: flex;
border: none;
width: 350px;
font-size: 18px;
font-weight: 500;
}

&__button {
display: flex;
border: none;
background-color: #ff7f23;
color: white;
border-radius: 100px;
width: 72px;
height: 46px;
justify-content: center;
align-items: center;
font-weight: 500;
font-size: 18px;
margin-left: 39px;
box-shadow: 5px 5px 15px 1px #0000001a;
}
}

.location {
display: flex;
border: none;
margin-top: 8px;
font-size: 16px;
font-weight: 400;
margin-left: 32px;
border-bottom: 1px solid #c4c4c4;
width: 510px;
padding: 10px;
cursor: pointer;

&__active {
display: flex;
align-items: center;
border-radius: 50px;
padding: 3px 25px 3px 10px;

&:hover {
display: flex;
background-color: #eeeeee;
}
}

&__image {
display: flex;
margin: 8px;
margin-left: 17px;
}
}
28 changes: 28 additions & 0 deletions src/pages/Home/components/Map/components/Location/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ReactComponent as Search } from 'assets/svg/home/search.svg';
import { ReactComponent as Point } from 'assets/svg/home/point.svg';
import styles from './Location.module.scss';

export default function Location(): JSX.Element {
return (
<div className={styles.container}>
<div className={styles.box}>
<div className={styles.box__title}>현재 위치가 올바르지 않은가요?</div>
<div className={styles.box__subTitle}>현재 계신 곳의 위치를 아래 검색창을 통해</div>
<div className={styles.box__subTitle}>알려주시면 반영하겠습니다.</div>
Comment on lines +10 to +11
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

간단한 줄바꿈이라면 <br />태그를 사용하는건 어떨까요?
line-height을 적절히 활용할 수 있고, 수정도 용이하다고 생각해요

Suggested change
<div className={styles.box__subTitle}>현재 계신 곳의 위치를 아래 검색창을 통해</div>
<div className={styles.box__subTitle}>알려주시면 반영하겠습니다.</div>
<div className={styles.box__subTitle}>
현재 계신 곳의 위치를 아래 검색창을 통해
<br />
알려주시면 반영하겠습니다.
</div>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 코드를 사용한다면 각 문장에 추가로 태그 처리를 해줘야 코드를 묶어서 중앙정렬을 해줄수 있어 코드의 길이가 더 늘어나지 않을까요?!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

text-align: center 를 사용하면 내부 태그 처리가 따로 없어도 될 거예요

</div>
<div className={styles.search}>
<div className={styles.search__box}>
<Search className={styles.search__image} />
<input type="text" className={styles.search__text} placeholder="지번, 도로명, 건물명으로 검색해주세요." />
</div>
<button className={styles.search__button} type="button">등록</button>
</div>
Comment on lines +14 to +19
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

검색 완료 시 enter키 등으로 결과를 확인하려면 구조를 어떻게 바꿔야할까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

키보드 이벤트를 추가해 결과를 입력받는 걸 추가하면 될 것 같습니다! 다음에 만들것이 위치 입력 기능인데 그때 추가하도록 하겠습니다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

간단하게 form + submit구조라면 네이티브 기능을 활용해 더 편하게 작성할 수 있을거예요~

<div className={styles.location}>
<div className={styles.location__active}>
<Point className={styles.location__image} />
현재 위치로 설정
</div>
</div>
</div>
);
}
56 changes: 56 additions & 0 deletions src/pages/Home/components/Map/hooks/useHome.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useEffect, useRef, useState } from 'react';
import useBooleanState from 'utils/hooks/useBooleanState';
import getAddress from 'api/location';
import makeToast from 'utils/ts/makeToast';

export default function useHome() {
const [isClickLocation, active, unactive] = useBooleanState(false);
const locationRef = useRef<HTMLDivElement | null>(null);
const [userLocation, setUserLocation] = useState({
latitude: null as number | null,
longitude: null as number | null,
address: null as string | null,
});

useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (locationRef.current && !locationRef.current.contains(event.target as Node)) {
unactive();
}
}
document.addEventListener('mouseup', handleClickOutside);
return () => {
document.removeEventListener('mouseup', handleClickOutside);
};
}, [unactive]);

const updateUserLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(async (position) => {
const addressData = await getAddress(
position.coords.latitude,
position.coords.longitude,
);
setUserLocation((prevUserLocation) => ({
...prevUserLocation,
latitude: position.coords.latitude,
longitude: position.coords.longitude,
address: addressData,
}));
}, (error) => {
makeToast('error', error.message);
});
}
};

useEffect(() => {
updateUserLocation();
}, []);

return {
isClickLocation,
active,
locationRef,
userLocation,
};
}
2 changes: 1 addition & 1 deletion src/pages/Home/components/Map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import useMarker from './hooks/useMarker';
import useFilterShops from './hooks/useFilterShops';
import Pin from '../Pin';

export default function Map(): JSX.Element {
export default function NaverMap(): JSX.Element {
const { isMobile } = useMediaQuery();
const { location } = useLocation();
const map = useNaverMap(location?.latitude, location?.longitude);
Expand Down
24 changes: 22 additions & 2 deletions src/pages/Home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import Map from './components/Map';
import { Container as MapDiv } from 'react-naver-maps';
import useHome from './components/Map/hooks/useHome';
import Location from './components/Map/components/Location/index';
import NaverMap from './components/Map';
import styles from './Home.module.scss';
import LocationInfo from './components/LocationInfo';

export default function Home(): JSX.Element {
const {
isClickLocation, active, userLocation, locationRef,
} = useHome();
return (
<div className={styles.home}>
<Map />
<LocationInfo address={userLocation.address} onClick={active} />
<div className={styles.map}>
<MapDiv>
<NaverMap />
</MapDiv>
</div>
{isClickLocation && (
<div
className={styles.locationBox}
ref={locationRef}
>
<Location />
</div>
)}
</div>
);
}
Loading
Loading