diff --git a/README.md b/README.md
index ec09dd6..1a646bd 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-
날씨로그
예보보다 가까운, 이웃이 직접 전하는 지금 날씨
@@ -14,11 +13,7 @@
날씨로그는 같은 동네의 이웃들이 직접 느낀 날씨를 사진과 함께 공유하는 모바일 웹 서비스입니다.
숫자로 표시된 예보만으로 알기 어려운 체감온도, 비, 햇빛 상태를 최근 제보로 빠르게 확인할 수 있습니다.
-## 서비스 미리보기
-
-
-
-
+## 핵심 기능
### 동네 날씨 피드
diff --git a/app/globals.css b/app/globals.css
index 4c1eb7e..fb6ce04 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -16,6 +16,7 @@ html { background: #deeff8; }
body {
margin: 0;
min-height: 100vh;
+ min-height: 100dvh;
color: var(--ink);
background: linear-gradient(145deg, #deeff8 0%, #f4fbff 50%, #ddecf5 100%);
font-family: "Pretendard Variable", Pretendard, "Apple SD Gothic Neo", "Noto Sans KR", sans-serif;
@@ -27,12 +28,13 @@ button { cursor: pointer; }
position: relative;
width: min(100%, 480px);
min-height: 100vh;
+ min-height: 100dvh;
margin: 0 auto;
overflow-x: clip;
background: #eef9ff;
box-shadow: 0 0 48px rgba(48, 98, 126, .12);
}
-.page { min-height: 100vh; padding: 0 20px 112px; }
+.page { min-height: 100vh; min-height: 100dvh; padding: 0 20px 112px; }
.main-tab-page { padding-bottom: 80px; }
.safe-top { padding-top: max(8px, env(safe-area-inset-top)); }
.home-header-top { padding-top: max(9px, env(safe-area-inset-top)); }
diff --git a/components/auth-callback-screen.tsx b/components/auth-callback-screen.tsx
index 82ff26b..881b0bb 100644
--- a/components/auth-callback-screen.tsx
+++ b/components/auth-callback-screen.tsx
@@ -6,6 +6,7 @@ import { useRouter, useSearchParams } from "next/navigation";
import { useEffect, useRef, useState } from "react";
import { SocialIcon } from "@/components/social-icon";
import { authApi, type OAuthCallbackResult } from "@/lib/api/auth-api";
+import { markAccountPanelReturn } from "@/lib/account-panel-return";
import { resolveApiUrl } from "@/lib/api/config";
import { logger } from "@/lib/logging";
import type { SocialProvider } from "@/lib/types";
@@ -55,6 +56,7 @@ export function AuthCallbackScreen() {
const code = searchParams.get("code");
if (result === "LINK_SUCCESS") {
authLogger.info("authentication_completed", { result });
+ markAccountPanelReturn();
showToast("소셜 계정을 연동했어요.", "SUCCESS");
router.replace("/mypage");
return;
@@ -128,7 +130,7 @@ export function AuthCallbackScreen() {
return (
-
+
계정 연동
diff --git a/components/home-screen.tsx b/components/home-screen.tsx
index fc0cd73..3e71241 100644
--- a/components/home-screen.tsx
+++ b/components/home-screen.tsx
@@ -17,7 +17,7 @@ import { getLocationName } from "@/lib/constants";
import { useToastStore } from "@/store/toast-store";
export function HomeScreen() {
- const { location, setLocation, isDetecting, detectionError, needsManualInput, setNeedsManualInput, detectLocation } = useCurrentLocation({ refreshOnResume: true });
+ const { location, setLocation, isDetecting, detectionError, needsManualInput, setNeedsManualInput, detectLocation } = useCurrentLocation();
const showToast = useToastStore((state) => state.showToast);
const loadMoreRef = useRef
(null);
const locationLabel = location ? getLocationName(location, "short") : "";
@@ -55,7 +55,7 @@ export function HomeScreen() {
const hasWeatherData = hasSummaryData || hasReportData;
const hasWeatherError = summary.isError || reports.isError;
const showLocationError = !location && !isDetecting && Boolean(detectionError);
- const isInitialWeatherLoading = !locationLabel || (!hasWeatherData && (summary.isPending || reports.isPending));
+ const isInitialWeatherLoading = !isDetecting && Boolean(locationLabel) && !hasWeatherData && (summary.isPending || reports.isPending);
const showFullWeatherError = Boolean(locationLabel) && !hasWeatherData && hasWeatherError && !summary.isPending && !reports.isPending;
return (
diff --git a/components/home-weather-controls.tsx b/components/home-weather-controls.tsx
index f808132..78e2998 100644
--- a/components/home-weather-controls.tsx
+++ b/components/home-weather-controls.tsx
@@ -2,6 +2,7 @@ import { format } from "date-fns";
import { ko } from "date-fns/locale";
import { ChevronDown, MapPin, RotateCw } from "lucide-react";
import { useSyncExternalStore } from "react";
+import { LocationDetectingIndicator } from "@/components/location-detecting-indicator";
let openedAt = 0;
const openedAtListeners = new Set<() => void>();
@@ -42,11 +43,11 @@ export function HomeWeatherControls({
: "현재 시각";
return (
-
+
{isDetecting
- ?
+ ?
: {location || "동네를 선택해 주세요"} }
diff --git a/components/location-detecting-indicator.tsx b/components/location-detecting-indicator.tsx
new file mode 100644
index 0000000..0f516f8
--- /dev/null
+++ b/components/location-detecting-indicator.tsx
@@ -0,0 +1,30 @@
+import { LoaderCircle } from "lucide-react";
+
+export function LocationDetectingIndicator({
+ className = "",
+ compact = false,
+ iconPosition = "left",
+}: {
+ className?: string;
+ compact?: boolean;
+ iconPosition?: "left" | "right";
+}) {
+ const spinner = (
+
+ );
+
+ return (
+
+ {iconPosition === "left" && spinner}
+ 위치를 찾는 중
+ {iconPosition === "right" && spinner}
+
+ );
+}
diff --git a/components/location-picker.tsx b/components/location-picker.tsx
index 22f8887..3045b23 100644
--- a/components/location-picker.tsx
+++ b/components/location-picker.tsx
@@ -4,6 +4,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { AlertCircle, Check, Flame, LockKeyhole, LocateFixed, MapPin, RefreshCw, Search, Star, X } from "lucide-react";
import { FormEvent, useEffect, useState, type ReactNode } from "react";
import { createPortal } from "react-dom";
+import { LocationDetectingIndicator } from "@/components/location-detecting-indicator";
import { getLocationName } from "@/lib/constants";
import { locationApi } from "@/lib/api/location-api";
import { truncateText } from "@/lib/text";
@@ -106,14 +107,14 @@ function LocationPickerDialog({ current, isDetecting, detectionError, required,
{!required && }
-
-
-
현재 설정된 위치
{isDetecting ?
:
{selectedLocation ? getLocationName(selectedLocation, "full") : current ? getLocationName(current, "full") : "아직 설정되지 않았어요"}
}
-
GPS
+
+
+
{isDetecting ?
:
{selectedLocation ? getLocationName(selectedLocation, "full") : current ? getLocationName(current, "full") : "아직 설정되지 않았어요"}
}
+
GPS
{detectionError &&
{detectionError}
}