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

[FE][Feat] #257 : Search 기능 구현 및 검색창 기능들 구현 #270

Merged
merged 4 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import startmarker from '@/assets/startmarker.png';
import endmarker from '@/assets/endmarker.png';
import { CurrentUserContext } from '@/context/CurrentUserContext';
import { ToolDescription } from '@/component/tooldescription/ToolDescription';
import { SearchBox } from '@/component/searchbox/SearchBox';

export const MapCanvasForDraw = ({
width,
Expand Down Expand Up @@ -216,9 +217,27 @@ export const MapCanvasForDraw = ({
if (!clickedPoint) return;
switch (toolType) {
case ButtonState.START_MARKER:
setCurrentUser(prevUser => ({
...prevUser,
start_location: {
...prevUser.start_location,
title: '', // title을 빈 문자열로 초기화 -> 검색창에 보이게 하려고
lat: clickedPoint.lat,
lng: clickedPoint.lng,
},
}));
setStartMarker(clickedPoint);
break;
case ButtonState.DESTINATION_MARKER:
setCurrentUser(prevUser => ({
...prevUser,
end_location: {
...prevUser.end_location,
title: '', // title을 빈 문자열로 초기화 -> 검색창에 보이게 하려고
lat: clickedPoint.lat,
lng: clickedPoint.lng,
},
}));
setEndMarker(clickedPoint);
break;
case ButtonState.LINE_DRAWING:
Expand Down Expand Up @@ -384,6 +403,33 @@ export const MapCanvasForDraw = ({
}
};

const handleCreateMarker = (point: IPoint) => {
if (toolType === ButtonState.START_MARKER) {
setStartMarker(point);
setCurrentUser(prevUser => ({
...prevUser,
start_location: {
...prevUser.start_location,
title: '',
},
}));
} else {
setEndMarker(point);
setCurrentUser(prevUser => ({
...prevUser,
end_location: {
...prevUser.end_location,
title: '',
},
}));
}
};

const handleDeleteMarker = () => {
if (toolType === ButtonState.START_MARKER) setStartMarker(null);
else setEndMarker(null);
};

useEffect(() => {
if (isDragging) {
if (canvasRef.current) {
Expand Down Expand Up @@ -415,6 +461,16 @@ export const MapCanvasForDraw = ({
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
>
{(toolType === ButtonState.START_MARKER || toolType === ButtonState.DESTINATION_MARKER) && (
<div className="relative">
<SearchBox
setMarker={handleCreateMarker}
deleteMarker={handleDeleteMarker}
startMarker={startMarker}
endMarker={endMarker}
/>
</div>
)}
<div ref={mapRef} style={{ width: '100%', height: '100%' }} />
{toolType === ButtonState.LINE_DRAWING ? (
<div className="z-1000 absolute left-1/2 top-[10px] flex -translate-x-1/2 transform gap-2">
Expand Down
104 changes: 73 additions & 31 deletions frontend/src/component/searchbox/SearchBox.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import { ToolTypeContext } from '@/context/ToolTypeContext';
import React, { useContext, useEffect, useState } from 'react';
import { IoMdClose, IoMdSearch } from 'react-icons/io';
import { IoMdClose } from 'react-icons/io';
import { CurrentUserContext } from '@/context/CurrentUserContext';
import { IPoint } from '@/lib/types/canvasInterface';
import { getAddressFromCoordinates } from '@/utils/map/getAddress';
import { ButtonState } from '../common/enums';

interface ISearchResultItem {
title: string;
address: string;
link: string;
roadAddress: string;
lat: number;
lng: number;
}

export const SearchBox = () => {
const [inputValue, setInputValue] = useState(''); // 검색 입력값 상태
interface ISearchBoxProps {
startMarker?: IPoint | null;
endMarker?: IPoint | null;
setMarker: (point: IPoint) => void;
deleteMarker: () => void;
}
export const SearchBox = (props: ISearchBoxProps) => {
const [inputValue, setInputValue] = useState<string>(''); // 검색 입력값 상태
const [searchResults, setSearchResults] = useState<ISearchResultItem[]>([]); // 검색 결과 상태
const [loading, setLoading] = useState(false); // 로딩 상태
const [error, setError] = useState<string | null>(null); // 에러 상태
Expand Down Expand Up @@ -44,13 +52,13 @@ export const SearchBox = () => {
setError(null);

try {
const apiUrl = `https://cors-anywhere.herokuapp.com/https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent(
inputValue,
)}&display=5&start=1&sort=random`;

// const apiUrl = `https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent(
// const apiUrl = `https://cors-anywhere.herokuapp.com/https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent(
// inputValue,
// )}&display=10&start=1&sort=random`;
// )}&display=5&start=1&sort=random`;

const apiUrl = `https://openapi.naver.com/v1/search/local.json?query=${encodeURIComponent(
inputValue,
)}&display=10&start=1&sort=random`;

const response = await fetch(apiUrl, {
headers: {
Expand All @@ -68,8 +76,8 @@ export const SearchBox = () => {
title: item.title.replace(/<\/?[^>]+(>|$)/g, ''), // HTML 태그 제거
address: item.address || item.roadAddress || '주소 정보 없음',
link: item.link || '#',
lat: parseFloat(item.mapy) / 1e7, // 위도 값 변환
lng: parseFloat(item.mapx) / 1e7, // 경도 값 변환
lat: parseFloat(item.mapy) / 1e7,
lng: parseFloat(item.mapx) / 1e7,
}));
console.log(data);
setSearchResults(formattedResults); // 검색 결과 상태 업데이트
Expand All @@ -83,45 +91,79 @@ export const SearchBox = () => {
/* TODO: 자동검색 로직 수정 필요 */

useEffect(() => {
const delayDebounceFn = setTimeout(() => {
if (inputValue.trim()) {
handleSearch();
const getAddress = async () => {
if (toolType === ButtonState.START_MARKER && props.startMarker) {
if (currentUser.start_location?.title) {
// title이 비어있을 때 === 부산역 같은 title이 없을때
return;
}
const value = await getAddressFromCoordinates(props.startMarker.lat, props.startMarker.lng);
setInputValue(value);
} else if (toolType === ButtonState.DESTINATION_MARKER && props.endMarker) {
if (currentUser.end_location?.title) {
return;
}
const value = await getAddressFromCoordinates(props.endMarker.lat, props.endMarker.lng);
setInputValue(value);
}
}, 300);
};

return () => clearTimeout(delayDebounceFn);
}, [inputValue]);
getAddress();
}, [toolType, props.startMarker, props.endMarker]);

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value); // 상태 업데이트
};

useEffect(() => {
// 마커가 이미 존재하는 경우 더 이상 검색하지 않도록 방지
if (toolType === ButtonState.START_MARKER && props.startMarker) {
return;
}

if (toolType === ButtonState.DESTINATION_MARKER && props.endMarker) {
return;
}

if (inputValue.trim()) {
const delayDebounceFn = setTimeout(() => {
handleSearch();
}, 300);

// Todo : 요부분 반환값 lint 오류 때문에 해결이 안돼요...
// eslint-disable-next-line consistent-return
return () => {
clearTimeout(delayDebounceFn);
};
}
}, [inputValue, props.startMarker, props.endMarker, toolType]);

const handleSelectResult = (result: ISearchResultItem) => {
setInputValue(result.title); // 선택한 결과로 inputValue를 업데이트
setSearchResults([]); // 결과 리스트를 닫음
setInputValue(result.title);
setSearchResults([]);
props.setMarker({ lat: result.lat, lng: result.lng });
updateUser(result.title, result.lat, result.lng);
console.log(`위도: ${result.lat}, 경도: ${result.lng}`);
};

const handleClear = () => {
setInputValue(''); // 입력값 초기화
setSearchResults([]); // 검색 결과 초기화
setInputValue('');
setSearchResults([]);
props.deleteMarker();
};

return (
<div className="relative">
<div className="absolute top-2 z-[6000] w-full px-2">
{/* 검색 입력 */}
<div className="border-grayscale-75 text-grayscale-400 flex h-11 w-full rounded border px-3">
<div className="border-grayscale-75 text-grayscale-400 flex h-11 w-full rounded border bg-white px-3">
<input
type="text"
value={inputValue}
onChange={handleInputChange}
placeholder="검색어를 입력하세요"
className="placeholder:text-grayscale-50 text-grayscale-400 h-11 w-full px-3 text-xs focus:outline-none"
placeholder={
toolType === ButtonState.START_MARKER ? '출발지를 입력하세요' : '도착지를 입력하세요'
}
className="placeholder:text-grayscale-50 text-grayscale-400 h-full w-full px-3 text-xs focus:outline-none"
/>
<button className="flex h-full w-8 items-center" onClick={handleSearch}>
<IoMdSearch className="h-6 w-6" />
</button>
<button className="jusify-center flex h-full w-8 items-center" onClick={handleClear}>
<IoMdClose className="h-6 w-6" />
</button>
Expand All @@ -135,7 +177,7 @@ export const SearchBox = () => {
{searchResults.map(result => (
<button
type="button"
key={result.link}
key={result.roadAddress}
onClick={() => handleSelectResult(result)}
className="flex flex-col items-start gap-2 p-2"
>
Expand Down
16 changes: 3 additions & 13 deletions frontend/src/pages/DrawRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useContext, useEffect } from 'react';
import { FooterContext } from '@/component/layout/footer/LayoutFooterProvider';
import { useNavigate, useParams } from 'react-router-dom';
import { IUser, UserContext } from '@/context/UserContext';
import { SearchBox } from '@/component/searchbox/SearchBox';
import { ToolTypeProvider } from '@/context/ToolTypeContext';
import { buttonActiveType } from '@/component/layout/enumTypes';
import { MapProviderForDraw } from '@/component/canvasWithMap/canvasWithMapforDraw/MapProviderForDraw.tsx';
Expand Down Expand Up @@ -84,11 +83,7 @@ export const DrawRoute = () => {
const updatedUser = { ...currentUser }; // currentUser 복사

// start_location.title이 비어 있으면 주소를 업데이트
if (
!updatedUser.start_location.title &&
updatedUser.start_location.lat &&
updatedUser.start_location.lng
) {
if (!updatedUser.start_location.title) {
try {
const startAddress = await getAddressFromCoordinates(
updatedUser.start_location.lat,
Expand All @@ -101,11 +96,7 @@ export const DrawRoute = () => {
}

// end_location.title이 비어 있으면 주소를 업데이트
if (
!updatedUser.end_location.title &&
updatedUser.end_location.lat &&
updatedUser.end_location.lng
) {
if (!updatedUser.end_location.title) {
try {
const endAddress = await getAddressFromCoordinates(
updatedUser.end_location.lat,
Expand Down Expand Up @@ -138,9 +129,8 @@ export const DrawRoute = () => {
return (
<ToolTypeProvider>
<div className="flex h-full w-full flex-col py-20">
<SearchBox />
<div style={{ position: 'relative', padding: '1rem' }}>
<MapProviderForDraw width={window.innerWidth - 32} height={window.innerHeight - 210} />
<MapProviderForDraw width={window.innerWidth - 32} height={window.innerHeight - 180} />
</div>
</div>
</ToolTypeProvider>
Expand Down
Loading