diff --git a/src/app/router/index.tsx b/src/app/router/index.tsx
index 4589659..6904a57 100644
--- a/src/app/router/index.tsx
+++ b/src/app/router/index.tsx
@@ -14,6 +14,7 @@ import { mapHomeLoader } from "@/pages/map/map-home-loader";
import NicknamePage from "@/pages/onboarding/NicknamePage";
import TermsAgreementPage from "@/pages/onboarding/TermsAgreementPage";
import ReelsPlaceSelectPage from "@/pages/ReelsPlaceSelectPage";
+import RegisterPlaceInpersonPage from "@/pages/RegisterPlaceInpersonPage";
import RegisterSelectRoomPage from "@/pages/RegisterSelectRoomPage";
import SplashScreenPage from "@/pages/SplashScreenPage";
import { APP_ROUTES } from "@/shared/config/routes";
@@ -35,6 +36,7 @@ export const router = createBrowserRouter([
{ path: "dev/SelectOption", element: },
{ path: "dev/register_place", element: },
{ path: "edit_place", element: },
+ { path: "register-place-inperson", element: },
{ path: "register-select-room", element: },
{
path: "dev/list",
diff --git a/src/pages/RegisterPlaceInpersonPage.tsx b/src/pages/RegisterPlaceInpersonPage.tsx
new file mode 100644
index 0000000..5839de9
--- /dev/null
+++ b/src/pages/RegisterPlaceInpersonPage.tsx
@@ -0,0 +1,169 @@
+import { useCallback, useEffect, useMemo, useState } from "react";
+import { useNavigate } from "react-router-dom";
+
+import { SearchField } from "@/components/common/SearchField";
+import { EditPlaceResultCard } from "@/components/reels/EditPlaceResultCard";
+import { PillButton } from "@/components/ui/PillButton";
+import { SAVED_PLACE_MOCKS } from "@/shared/mocks/place-mocks";
+import { useInpersonPlaceStore } from "@/store/inpersonPlaceStore";
+import { useRegisterRoomStore } from "@/store/registerRoomStore";
+
+const REELS_LINK_MOCK = "https://www.instagram.com/reel/DNp9tqSz6rT/?igsh=MW4yOGd6aGNzMmRsYw==";
+
+export default function RegisterPlaceInpersonPage() {
+ const navigate = useNavigate();
+ const keyword = useInpersonPlaceStore((state) => state.keyword);
+ const selectedPlaceId = useInpersonPlaceStore((state) => state.selectedPlaceId);
+ const setKeyword = useInpersonPlaceStore((state) => state.setKeyword);
+ const setSelectedPlace = useInpersonPlaceStore((state) => state.setSelectedPlace);
+ const reset = useInpersonPlaceStore((state) => state.reset);
+ const setSelectedPlacesForRegister = useRegisterRoomStore((state) => state.setSelectedPlaces);
+ const [copyLabel, setCopyLabel] = useState("복사");
+
+ useEffect(() => {
+ reset();
+ }, [reset]);
+
+ const trimmedKeyword = keyword.trim();
+ const canSearch = trimmedKeyword.length > 0;
+ const canConfirm = selectedPlaceId !== null;
+
+ const searchResults = useMemo(() => {
+ if (!trimmedKeyword) {
+ return [];
+ }
+
+ return SAVED_PLACE_MOCKS.filter(
+ (place) => place.name.includes(trimmedKeyword) || place.address.includes(trimmedKeyword),
+ );
+ }, [trimmedKeyword]);
+
+ const selectedPlace = useMemo(
+ () => SAVED_PLACE_MOCKS.find((place) => place.id === selectedPlaceId) ?? null,
+ [selectedPlaceId],
+ );
+
+ const handleCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(REELS_LINK_MOCK);
+ setCopyLabel("복사됨");
+ window.setTimeout(() => setCopyLabel("복사"), 1500);
+ } catch {
+ setCopyLabel("실패");
+ window.setTimeout(() => setCopyLabel("복사"), 1500);
+ }
+ }, []);
+
+ const handleCancel = () => {
+ reset();
+ navigate(-1);
+ };
+
+ const handleConfirm = () => {
+ if (!selectedPlaceId) {
+ return;
+ }
+
+ setSelectedPlacesForRegister([selectedPlaceId]);
+ navigate("/register-select-room", {
+ state: {
+ selectedPlaceIds: [selectedPlaceId],
+ selectedPlaceCount: 1,
+ selectedPlace,
+ },
+ });
+ };
+
+ return (
+
+
+
+
+
+ 장소 인식에 실패했습니다.
+
+
+ 해당 장소를 직접 입력해주세요.
+
+
+
+
+
{REELS_LINK_MOCK}
+
+
+
+
+
+ {trimmedKeyword ? (
+
+ {searchResults.length === 0 ? (
+ -
+ 검색 결과가 없습니다
+
+ ) : (
+ searchResults.map((place) => (
+ setSelectedPlace(place.id)}
+ />
+ ))
+ )}
+
+ ) : null}
+
+
+
+
+
+ );
+}
diff --git a/src/store/inpersonPlaceStore.ts b/src/store/inpersonPlaceStore.ts
new file mode 100644
index 0000000..f1804f8
--- /dev/null
+++ b/src/store/inpersonPlaceStore.ts
@@ -0,0 +1,21 @@
+import { create } from "zustand";
+
+type InpersonPlaceState = {
+ keyword: string;
+ selectedPlaceId: string | null;
+ setKeyword: (keyword: string) => void;
+ setSelectedPlace: (placeId: string | null) => void;
+ reset: () => void;
+};
+
+export const useInpersonPlaceStore = create((set) => ({
+ keyword: "",
+ selectedPlaceId: null,
+ setKeyword: (keyword) => set({ keyword }),
+ setSelectedPlace: (placeId) => set({ selectedPlaceId: placeId }),
+ reset: () =>
+ set({
+ keyword: "",
+ selectedPlaceId: null,
+ }),
+}));