API
기술 스택
서점찾기 페이지 screen height 100vh 적용
window 객체의 screenHeight을 상수로 저장해 페이지 높이를 screenHeight - headerHeight
로 지정해주었다.
이렇게 하면 페이지 창 크기를 동적으로 조절할 때 바로바로 반영이 안되는 문제가 발생했다.
그럼 창 크기 변경에 event listener를 등록하느냐? → 창 크기가 변경 될 때마다 상수를 바꿔주고 css에 변환해서 적용해서 렌더링하고.... 비효율적이라는 생각이 들었다.
css로 해결!
css 내에서 calc()
메소드를 사용해 계산을 할 수 있었다.
height: calc(100vh - ${headerHeight}px)
추가적으로 검색 결과에 스크롤바를 적용하기 위해 overflow-y: auto;
속성을 지정했다.
flex-box를 이용한 반응형 레이아웃 적용
데스크탑에서는 Info와 Map을 좌우로 나란히, 모바일에서는 Info 아래에 Map을 위치했다.
slice
메소드를 사용했다.loadCount
를 변수화하여 더보기 데이터 개수 변경이 용이하도록 했다.const loadCount = 20;
const [countOfData, setCountOfData] = useState<number>(loadCount);
const handleLoadMoreButtonClick = useCallback(() => {
// 데이터의 끝이라면 isEndOfData 상태를 true로 변경
if (countOfData + loadCount >= DB.length) {
setCountOfData(DB.length);
setIsEndOfData(true);
return;
}
setCountOfData(countOfData + loadCount);
}, [countOfData, DB.length]);
// 데이터의 끝이 아닐 때 더보기 버튼 렌더링
{isEndOfData || (
<S.LoadMoreButton onClick={handleLoadMoreButtonClick}>
더보기
</S.LoadMoreButton>
)}
kakao API를 이용해 반경을 나타냈는데, 내 위치로 검색하기 버튼을 여러번 누르면 반경이 중복으로 렌더링 되는 문제점이 있었다.
그래서 currentCircle
state에 현재 반경을 저장하고, state가 존재한다면 null을 할당해 초기화해주는 로직을 추가했다.
const location = useGeolocation();
const handleSearchCurrentLocationClick = useCallback(() => {
// 현재 위치, map이 없다면 return
if (!location || !map) return;
navigate('/map');
// 1. 현재 중심 위치를 kakaoMap 좌표로 변환
const currentCenter = new window.kakao.maps.LatLng(
location.coordinates?.lat,
location.coordinates?.lng,
);
// 현재 위치 전역 상태 저장
setCurrentLocation(location);
// 2. 이전 반경 표시 삭제
currentCircle && currentCircle.setMap(null);
// 3. 필터 초기화 (전체 검색 결과를 바탕으로 필터링 하기 위함)
handleResetResult();
// 4. 중심에서 10km 반경 내의 데이터 필터링 후 DB에 저장
const newDB: any[] = [];
data.forEach((store) => {
// 서점의 위치
const storeLocation = new window.kakao.maps.LatLng(
store.FCLTY_LA,
store.FCLTY_LO,
);
const poly = new window.kakao.maps.Polyline({
path: [currentCenter, storeLocation],
});
// 서점과 현재 위치의 거리
const distance = poly.getLength();
if (distance <= 10000) {
newDB.push(store);
}
});
setDB(newDB);
// 5. 현재 위치로 지도 이동
map.setLevel(8);
map.panTo(currentCenter);
// 6. 현재 위치 반경 5km 표시
const circle = new window.kakao.maps.Circle({
center: currentCenter,
radius: 10000,
strokeWeight: 1,
strokeColor: BLUE_COLOR,
strokeOpacity: 0.8,
fillColor: BLUE_COLOR,
fillOpacity: 0.2,
});
circle.setMap(map);
// 현재 위치 반경 표시 저장
setCurrentCircle(circle);
// eslint-disable-next-line
}, [currentCircle, location, map, setCurrentLocation, handleResetResult, setDB]);
현재 위치 반경과 마찬가지로 마커를 여러번 클릭하면 오버레이가 중복해서 생성되는 문제점이 있었다.
currentOverlayStoreId
상태를 저장하여 이전 overlay 상태가 존재한다면 null을 할당했다.
축적 level이 큰 경우, 마커를 클릭했을 때 확대되도록 구현했다. 그 기준을 level 8로 정하고, 8보다 큰 level일 경우 지도를 확대했다.
만약 level이 8보다 크다면 현재 level을 유지한다.
// * bookstoreId가 변경되면 지도 이동
useEffect(() => {
moveMap(bookstoreId);
// eslint-disable-next-line
}, [bookstoreId, map]);
// * 라우터 파라미터로 받은 bookstoreId 값에 따라 지도 이동
const moveMap = useCallback(
(bookstoreId: string | undefined) => {
// 지도가 생성되지 않았으면 함수 종료
if (!map) return;
// 1. 이전에 클릭한 마커가 있으면 지도에서 제거
const prevOverlay = currentOverlayStoreId;
prevOverlay && prevOverlay.setMap(null);
// 2. 클릭한 bookstoreId에 해당하는 마커로 지도 이동
DB.forEach((store) => {
if (store.ESNTL_ID === bookstoreId) {
const moveLatLon = new kakao.maps.LatLng(
store.FCLTY_LA,
store.FCLTY_LO,
);
// 지도 확대 레벨 설정
if (map.getLevel() > 8) {
map.setLevel(8);
}
map.panTo(moveLatLon); // 지도 중심 좌표 이동
// 3. 커스텀 오버레이 생성
const overlayContent = ReactDOMServer.renderToString(
<Overlay info={store} />,
);
const overlay = new kakao.maps.CustomOverlay({
content: overlayContent,
map: map,
position: moveLatLon,
xAnchor: 0.5,
yAnchor: 0.5,
});
setCurrentOverlayStoreId(overlay);
}
});
},
[DB, map, currentOverlayStoreId],
);
이번 프로젝트는 처음부터 퀄리티에 집중하기로 팀원들과 협의하고 시작했다. 그래서 구현 기능의 가지수에 부담을 가지지 않고 작성한 코드를 여러번 보며 리팩토링 하는 경험을 할 수 있었다.
발표회 때 튜터님께서도 잡다한 기능보다 핵심 기능으로 구성된게 기획적으로 좋아 보였다고 말씀해주셨다. 또 다른 팀 분들도 실제 서비스 해도 괜찮겠다고 반응해주셔서 ㅎㅎ.. 굉장히 뿌듯했다!
항상 느끼지만 나는 다른 사람 코드를 피드백하고 설명해주는게 참 즐겁다. 내가 도움이 되는 것도 좋고, 다양항 로직을 접하고 생각해볼 수 있어 스스로도 많은 도움을 받는다.
하지만 프로젝트가 끝날때마다 아쉬운 점은,, 코드를 작성하기 전에 설계를 꼼꼼히 해야한다는 것이다. 머리보다 손이 먼저 나가는 편이라 구현 속도가 빠르긴 하지만, 완성하고 보면 그게 최선이 아닐 때가 많다. 중간중간 피드백도 받고, 스스로 로직을 점검해볼 필요가 있다.