diff --git a/package.json b/package.json
index 0aa46f2..38e3efb 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"@hookform/resolvers": "^5.2.1",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-checkbox": "^1.3.3",
+ "@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-select": "^2.2.6",
@@ -37,6 +38,7 @@
"next-themes": "^0.4.6",
"prettier": "^3.6.2",
"react": "^19.1.1",
+ "react-daum-postcode": "^3.2.0",
"react-dom": "^19.1.1",
"react-hook-form": "^7.62.0",
"react-kakao-maps-sdk": "^1.2.0",
@@ -46,7 +48,8 @@
"tailwind-merge": "^3.3.1",
"tailwind-scrollbar-hide": "^4.0.0",
"tailwindcss": "^4.1.12",
- "zod": "^4.0.17"
+ "zod": "^4.0.17",
+ "zustand": "^5.0.8"
},
"devDependencies": {
"@eslint/js": "^9.33.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e67a427..835eaaa 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -17,6 +17,9 @@ importers:
'@radix-ui/react-checkbox':
specifier: ^1.3.3
version: 1.3.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-dialog':
+ specifier: ^1.1.15
+ version: 1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-label':
specifier: ^2.1.7
version: 2.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@@ -56,6 +59,9 @@ importers:
react:
specifier: ^19.1.1
version: 19.1.1
+ react-daum-postcode:
+ specifier: ^3.2.0
+ version: 3.2.0(react@19.1.1)
react-dom:
specifier: ^19.1.1
version: 19.1.1(react@19.1.1)
@@ -86,6 +92,9 @@ importers:
zod:
specifier: ^4.0.17
version: 4.0.17
+ zustand:
+ specifier: ^5.0.8
+ version: 5.0.8(@types/react@19.1.10)(immer@10.1.1)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1))
devDependencies:
'@eslint/js':
specifier: ^9.33.0
@@ -567,6 +576,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-dialog@1.1.15':
+ resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-direction@1.1.1':
resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
peerDependencies:
@@ -2588,6 +2610,11 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ react-daum-postcode@3.2.0:
+ resolution: {integrity: sha512-NHY8TUicZXMqykbKYT8kUo2PEU7xu1DFsdRmyWJrLEUY93Xhd3rEdoJ7vFqrvs+Grl9wIm9Byxh3bI+eZxepMQ==}
+ peerDependencies:
+ react: '>=16.8.0'
+
react-dom@19.1.1:
resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==}
peerDependencies:
@@ -3084,6 +3111,24 @@ packages:
zod@4.0.17:
resolution: {integrity: sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==}
+ zustand@5.0.8:
+ resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==}
+ engines: {node: '>=12.20.0'}
+ peerDependencies:
+ '@types/react': '>=18.0.0'
+ immer: '>=9.0.6'
+ react: '>=18.0.0'
+ use-sync-external-store: '>=1.2.0'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ use-sync-external-store:
+ optional: true
+
snapshots:
'@babel/code-frame@7.27.1':
@@ -3418,6 +3463,28 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.10
+ '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.10)(react@19.1.1)
+ '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@19.1.1)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@19.1.1)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@19.1.1)
+ aria-hidden: 1.2.6
+ react: 19.1.1
+ react-dom: 19.1.1(react@19.1.1)
+ react-remove-scroll: 2.7.1(@types/react@19.1.10)(react@19.1.1)
+ optionalDependencies:
+ '@types/react': 19.1.10
+ '@types/react-dom': 19.1.7(@types/react@19.1.10)
+
'@radix-ui/react-direction@1.1.1(@types/react@19.1.10)(react@19.1.1)':
dependencies:
react: 19.1.1
@@ -5377,6 +5444,10 @@ snapshots:
queue-microtask@1.2.3: {}
+ react-daum-postcode@3.2.0(react@19.1.1):
+ dependencies:
+ react: 19.1.1
+
react-dom@19.1.1(react@19.1.1):
dependencies:
react: 19.1.1
@@ -5978,3 +6049,10 @@ snapshots:
yocto-queue@0.1.0: {}
zod@4.0.17: {}
+
+ zustand@5.0.8(@types/react@19.1.10)(immer@10.1.1)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)):
+ optionalDependencies:
+ '@types/react': 19.1.10
+ immer: 10.1.1
+ react: 19.1.1
+ use-sync-external-store: 1.5.0(react@19.1.1)
diff --git a/src/entities/auth/hooks/useKakaoLogin.ts b/src/entities/auth/hooks/useKakaoLogin.ts
index b8d5ab0..252b62d 100644
--- a/src/entities/auth/hooks/useKakaoLogin.ts
+++ b/src/entities/auth/hooks/useKakaoLogin.ts
@@ -5,12 +5,9 @@ import { BASE_URL } from '@/shared';
import { getTicketApi } from '../apis';
-// --- 1. "로그인 시작"을 위한 useMutation ---
-// 이 훅은 API를 호출하지 않아. 오직 카카오 로그인 페이지로 '이동'시키는 액션만 책임져.
export const useKakaoLogin = () => {
return useMutation({
mutationFn: () => {
- // 실제로는 Promise를 반환할 필요는 없지만, mutationFn은 Promise를 기대해.
window.location.href = `${BASE_URL}/oauth2/authorization/kakao`;
return Promise.resolve();
},
@@ -20,7 +17,6 @@ export const useKakaoLogin = () => {
});
};
-// --- 2. "콜백 처리"를 위한 useQuery ---
export const KakaoCallbackQueryKey = {
callback: (ticket: string) => ['kakaoCallback', ticket],
};
@@ -28,9 +24,7 @@ export const KakaoCallbackQueryKey = {
export const useKakaoCallback = (ticket: string) => {
return useQuery({
queryKey: KakaoCallbackQueryKey.callback(ticket),
- // code가 있을 때만 이 쿼리를 실행해.
queryFn: () => getTicketApi(ticket),
- // 이 쿼리는 한 번만 실행되면 되므로, 재시도나 캐시 관련 옵션을 조정할 수 있어.
retry: 0,
staleTime: Infinity,
});
diff --git a/src/entities/index.ts b/src/entities/index.ts
index 6bf3065..d57fe8e 100644
--- a/src/entities/index.ts
+++ b/src/entities/index.ts
@@ -1,2 +1,3 @@
export * from './auth';
export * from './chart';
+export * from './map';
diff --git a/src/entities/map/index.ts b/src/entities/map/index.ts
new file mode 100644
index 0000000..e12841b
--- /dev/null
+++ b/src/entities/map/index.ts
@@ -0,0 +1,3 @@
+export * from './types';
+export * from './utils';
+export * from './ui';
diff --git a/src/entities/map/types/index.ts b/src/entities/map/types/index.ts
new file mode 100644
index 0000000..4b3f498
--- /dev/null
+++ b/src/entities/map/types/index.ts
@@ -0,0 +1 @@
+export * from './map.type';
diff --git a/src/entities/map/types/map.type.ts b/src/entities/map/types/map.type.ts
new file mode 100644
index 0000000..2510452
--- /dev/null
+++ b/src/entities/map/types/map.type.ts
@@ -0,0 +1,5 @@
+export type MapAddress = {
+ address: string;
+ latitude: number;
+ longitude: number;
+};
diff --git a/src/shared/components/features/kakao-map/KakaoMap.tsx b/src/entities/map/ui/KakaoMap.tsx
similarity index 63%
rename from src/shared/components/features/kakao-map/KakaoMap.tsx
rename to src/entities/map/ui/KakaoMap.tsx
index 1fb5eac..5fdc493 100644
--- a/src/shared/components/features/kakao-map/KakaoMap.tsx
+++ b/src/entities/map/ui/KakaoMap.tsx
@@ -1,5 +1,7 @@
import { Map, MapMarker, useKakaoLoader } from 'react-kakao-maps-sdk';
+import { Spinner } from '@/shared';
+
type Props = {
lat: number;
lng: number;
@@ -16,16 +18,12 @@ export const KakaoMap = ({ lat, lng }: Props) => {
lng: lng,
};
- if (loading) return
Loading...
;
- if (error) return Error: {error.message}
;
+ if (loading) return ;
+
+ if (error) return 오류 : {error.message}
;
return (
-