diff --git a/frontend/cypress/e2e/travelPlanRegister.cy.ts b/frontend/cypress/e2e/travelPlanRegister.cy.ts index f0397516..c7e04457 100644 --- a/frontend/cypress/e2e/travelPlanRegister.cy.ts +++ b/frontend/cypress/e2e/travelPlanRegister.cy.ts @@ -164,7 +164,7 @@ describe("여행 계획 등록 테스트", () => { // when cy.get(CYPRESS_SELECTOR_MAP.googleSearchPopup.searchInput).type(INPUT_VALUE); - cy.get(".pac-item").first().click(); + cy.get(".pac-item").first().click({ force: true }); // then cy.get(CYPRESS_SELECTOR_MAP.googleSearchPopup.container).should("not.exist"); diff --git a/frontend/package.json b/frontend/package.json index 3779bd9a..dd416c06 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "lint:styled": "stylelint './src/**/*.styled.ts' --fix", "test": "jest --passWithNoTests", "test-e2e": "cypress open", - "test-e2e:run": "cypress run", + "test-e2e:run": "cypress run --browser chrome", "storybook": "storybook dev -p 6006", "lint": "eslint src/", "build-storybook": "storybook build" diff --git a/frontend/src/components/common/Calendar/Calendar.styled.ts b/frontend/src/components/common/Calendar/Calendar.styled.ts index 48fa6056..64042570 100644 --- a/frontend/src/components/common/Calendar/Calendar.styled.ts +++ b/frontend/src/components/common/Calendar/Calendar.styled.ts @@ -68,8 +68,8 @@ export const DayCell = styled.td<{ $isCurrentMonth: boolean; $isSelectable: bool } `}; - color: ${({ theme, $isCurrentMonth }) => - $isCurrentMonth ? theme.colors.text.secondary : PRIMITIVE_COLORS.gray[300]}; + color: ${({ theme, $isCurrentMonth, $isSelectable }) => + $isCurrentMonth && $isSelectable ? theme.colors.text.secondary : PRIMITIVE_COLORS.gray[300]}; text-align: center; `; diff --git a/frontend/src/components/common/Calendar/Calendar.tsx b/frontend/src/components/common/Calendar/Calendar.tsx index 6f5a7a67..85d7db80 100644 --- a/frontend/src/components/common/Calendar/Calendar.tsx +++ b/frontend/src/components/common/Calendar/Calendar.tsx @@ -75,7 +75,7 @@ const Calendar = ({ onClick={() => isSelectable && onSelectDate(date)} data-cy={CYPRESS_DATA_MAP.calendar.dayCell} > - {date.getDate()} + {isCurrentMonth ? date.getDate() : ""} ); })} diff --git a/frontend/src/components/common/GoogleMapView/GoogleMapView.constant.ts b/frontend/src/components/common/GoogleMapView/GoogleMapView.constant.ts new file mode 100644 index 00000000..48b7c322 --- /dev/null +++ b/frontend/src/components/common/GoogleMapView/GoogleMapView.constant.ts @@ -0,0 +1,22 @@ +export const GOOGLE_MAP_CONTAINER_STYLE = { + width: "100%", + height: "23rem", +}; + +export const INIT_CENTER_POSITION = { + lat: 37.5665, + lng: 126.978, +}; + +export const GOOGLE_MAP_OPTIONS = { + disableDefaultUI: true, + styles: [ + { + featureType: "poi", + elementType: "labels", + stylers: [{ visibility: "off" }], + }, + ], +}; + +export const POLYLINE_OPTIONS = { strokeColor: "#72A2FFCC", strokeWeight: 3 }; diff --git a/frontend/src/components/common/GoogleMapView/GoogleMapView.tsx b/frontend/src/components/common/GoogleMapView/GoogleMapView.tsx index ad018725..79c05c1c 100644 --- a/frontend/src/components/common/GoogleMapView/GoogleMapView.tsx +++ b/frontend/src/components/common/GoogleMapView/GoogleMapView.tsx @@ -1,16 +1,20 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { useCallback } from "react"; +import { memo } from "react"; import { GoogleMap, MarkerF, Polyline } from "@react-google-maps/api"; -import theme from "@styles/theme"; +import { + GOOGLE_MAP_CONTAINER_STYLE, + GOOGLE_MAP_OPTIONS, + POLYLINE_OPTIONS, +} from "@components/common/GoogleMapView/GoogleMapView.constant"; +import { + calculateCenter, + createMarkerLabelStyle, +} from "@components/common/GoogleMapView/GoogleMapView.util"; -import { markerUrl } from "@assets/svg"; +import useGoogleMap from "@hooks/useGoogleMap"; -const containerStyle = { - width: "100%", - height: "23rem", -}; +import { markerUrl } from "@assets/svg"; interface GoogleMapViewProps { places: { @@ -19,72 +23,43 @@ interface GoogleMapViewProps { }[]; } -const calculateCenter = (places: { lat: number; lng: number }[]): { lat: number; lng: number } => { - const latSum = places.reduce((sum, place) => sum + place.lat, 0); - const lngSum = places.reduce((sum, place) => sum + place.lng, 0); - const count = places.length; - - return { - lat: latSum / count, - lng: lngSum / count, - }; -}; - const GoogleMapView = ({ places }: GoogleMapViewProps) => { const center = calculateCenter(places); - const onLoad = useCallback( - (map: google.maps.Map) => { - const bounds = new window.google.maps.LatLngBounds(); - places.forEach((place) => { - bounds.extend(new window.google.maps.LatLng(place.lat, place.lng)); - }); - map.fitBounds(bounds); - }, - [places], - ); + const { onLoad, onUnmount, onBoundsChanged } = useGoogleMap(places); + + /** + * 컴포넌트 내부에 위치시키지 않으면 "Uncaught TypeError: Cannot read properties of undefined (reading 'maps')" + * 에러가 발생하여 다음과 같은 위치로 변경 + */ + const MARKER_ICON_STYLE = { + url: markerUrl, + scaledSize: new window.google.maps.Size(30, 30), + labelOrigin: new window.google.maps.Point(15, -10), + }; return (
- {places.map((position, index) => ( - - ))} {places.map((position, index) => ( ))} - +
); }; -export default React.memo(GoogleMapView); +export default memo(GoogleMapView); diff --git a/frontend/src/components/common/GoogleMapView/GoogleMapView.util.ts b/frontend/src/components/common/GoogleMapView/GoogleMapView.util.ts new file mode 100644 index 00000000..e8dc7ced --- /dev/null +++ b/frontend/src/components/common/GoogleMapView/GoogleMapView.util.ts @@ -0,0 +1,27 @@ +import { INIT_CENTER_POSITION } from "@components/common/GoogleMapView/GoogleMapView.constant"; + +import theme from "@styles/theme"; + +export const calculateCenter = ( + places: { lat: number; lng: number }[], +): { lat: number; lng: number } => { + if (places.length === 0) { + return { lat: INIT_CENTER_POSITION.lat, lng: INIT_CENTER_POSITION.lng }; + } + const latSum = places.reduce((sum, place) => sum + place.lat, 0); + const lngSum = places.reduce((sum, place) => sum + place.lng, 0); + const count = places.length; + + return { + lat: latSum / count, + lng: lngSum / count, + }; +}; + +export const createMarkerLabelStyle = (markerOrder: number) => { + return { + text: `${markerOrder + 1}`, + color: theme.colors.primary, + fontSize: "1.4rem", + }; +}; diff --git a/frontend/src/components/common/Input/Input.styled.ts b/frontend/src/components/common/Input/Input.styled.ts index bf5e213a..aa7b4fb7 100644 --- a/frontend/src/components/common/Input/Input.styled.ts +++ b/frontend/src/components/common/Input/Input.styled.ts @@ -25,6 +25,7 @@ export const Input = styled.input<{ variant: InputVariants }>` ${({ theme }) => theme.typography.mobile.detail} color: ${({ theme }) => theme.colors.text.primary}; + font-size: 1.6rem; &:disabled { background-color: ${({ theme }) => theme.colors.background.disabled}; diff --git a/frontend/src/hooks/pages/useTravelPlanDays.ts b/frontend/src/hooks/pages/useTravelPlanDays.ts index f3f6dbdc..b85a5516 100644 --- a/frontend/src/hooks/pages/useTravelPlanDays.ts +++ b/frontend/src/hooks/pages/useTravelPlanDays.ts @@ -124,7 +124,7 @@ export const useTravelPlanDays = (days: TravelTransformPlaces[]) => { const travelPlanPlace = previousTravelPlanDays[dayIndex]?.places[placeIndex]; if (travelPlanPlace?.todos) { - travelPlanPlace.todos.splice(Number(todoId), 1); + travelPlanPlace.todos = travelPlanPlace.todos.filter((todo) => todo.id !== todoId); } }); }, diff --git a/frontend/src/hooks/useGoogleMap.ts b/frontend/src/hooks/useGoogleMap.ts new file mode 100644 index 00000000..56d049d4 --- /dev/null +++ b/frontend/src/hooks/useGoogleMap.ts @@ -0,0 +1,43 @@ +import { useCallback, useState } from "react"; + +import { MapPosition } from "@type/domain/common"; + +const INIT_CENTER_POSITION = { + lat: 37.5665, + lng: 126.978, +}; + +const useGoogleMap = (places: MapPosition[]) => { + const [googleMap, setGoogleMap] = useState(null); + + const onLoad = useCallback((map: google.maps.Map) => { + setGoogleMap(map); + }, []); + + const onUnmount = useCallback(() => { + setGoogleMap(null); + }, []); + + const onBoundsChanged = useCallback(() => { + if (googleMap) { + if (places.length === 0) { + googleMap.setCenter(INIT_CENTER_POSITION); + googleMap.setZoom(7); + } else { + const bounds = new window.google.maps.LatLngBounds(); + places.forEach((place) => { + bounds.extend(new window.google.maps.LatLng(place.lat, place.lng)); + }); + googleMap.fitBounds(bounds); + } + } + }, [places, googleMap]); + + return { + onLoad, + onUnmount, + onBoundsChanged, + }; +}; + +export default useGoogleMap; diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js index 904b799a..35dae068 100644 --- a/frontend/webpack.common.js +++ b/frontend/webpack.common.js @@ -45,8 +45,18 @@ module.exports = { use: ["@svgr/webpack"], }, { - test: /\.(png|jpg|jpeg|gif|woff|webp)$/i, + test: /\.(png|jpg|jpeg|gif|webp|avif)$/i, type: "asset/resource", + generator: { + filename: "assets/images/[name].[contenthash:8][ext]", + }, + }, + { + test: /\.(woff)$/i, + type: "asset/resource", + generator: { + filename: "assets/fonts/[name].[contenthash:8][ext]", + }, }, { test: /\.(ts|tsx)$/i, diff --git a/frontend/webpack.production.js b/frontend/webpack.production.js index a373be4d..bb0581b0 100644 --- a/frontend/webpack.production.js +++ b/frontend/webpack.production.js @@ -4,11 +4,17 @@ const common = require("./webpack.common"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const dotenv = require("dotenv"); // const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); +const path = require("path"); const env = dotenv.config({ path: ".env.production" }).parsed; module.exports = merge(common, { mode: "production", + output: { + publicPath: "/", + filename: "[name].[contenthash:8].js", + path: path.resolve(__dirname, "dist"), + }, devtool: "hidden-source-map", cache: { type: "filesystem", @@ -31,4 +37,9 @@ module.exports = merge(common, { // openAnalyzer: true, // }), ], + optimization: { + splitChunks: { + chunks: "all", + }, + }, });