- {type === 'search' && (
+ {type === 'search' ? (
+ ) : (
+
)}
@@ -101,10 +107,18 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
{type === 'character' ? (
) : (
@@ -153,39 +167,51 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
{/* 콘텐츠 영역 */}
-
- {showStats ? (
-
- {/* 캐릭터 정보와 어빌리티 (큰 화면에서 세로로 배치) */}
-
- {/* 캐릭터 정보 (작은 화면에서 맨 위) */}
-
{basic && }
-
- {/* 어빌리티 (캐릭터 정보 아래에 배치) */}
-
-
+ {isSyncing || isLoading ? (
+
+
+
+
+ 캐릭터 정보를 불러오는 중...
+
+
+
+
+ ) : (
+
+ {showStats ? (
+
+ {/* 캐릭터 정보와 어빌리티 (큰 화면에서 세로로 배치) */}
+
+ {/* 캐릭터 정보 (작은 화면에서 맨 위) */}
+
{basic && }
+
+ {/* 어빌리티 (캐릭터 정보 아래에 배치) */}
+
+
- {/* 기본 스탯 (큰 화면에서 중앙에 위치) */}
-
- {characterStats && }
-
+ {/* 기본 스탯 (큰 화면에서 중앙에 위치) */}
+
+ {characterStats && }
+
- {/* 하이퍼 스탯 (큰 화면에서 오른쪽에 위치) */}
-
- {hyperStat &&
}
+ {/* 하이퍼 스탯 (큰 화면에서 오른쪽에 위치) */}
+
+ {hyperStat && }
+
-
- ) : (
-
- {inventory && basic?.character_image && (
-
- )}
-
- )}
-
+ ) : (
+
+ {inventory && basic?.character_image && (
+
+ )}
+
+ )}
+
+ )}
)
}
diff --git a/src/hooks/character/useCharacterData.ts b/src/hooks/character/useCharacterData.ts
index c02e002..a2750c3 100644
--- a/src/hooks/character/useCharacterData.ts
+++ b/src/hooks/character/useCharacterData.ts
@@ -76,6 +76,8 @@ export const useCharacterData = () => {
})
const syncCharacterHandler = async () => {
+ if (mutateSyncCharacter.isPending) return
+
try {
await mutateSyncCharacter.mutateAsync()
} catch (error) {
@@ -101,6 +103,7 @@ export const useCharacterData = () => {
basic,
isLoading,
error,
- syncCharacterHandler
+ syncCharacterHandler,
+ isSyncing: mutateSyncCharacter.isPending
}
}
From 8ca0d1227c451e331955fb231c3043509062d46f Mon Sep 17 00:00:00 2001
From: echo
Date: Wed, 25 Jun 2025 01:27:08 +0900
Subject: [PATCH 04/10] =?UTF-8?q?style=20:=20=EC=BA=90=EB=A6=AD=ED=84=B0?=
=?UTF-8?q?=20=EC=A0=95=EB=B3=B4=20=EC=8A=A4=EC=BC=88=EB=A0=88=ED=86=A4=20?=
=?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/character/CharacterPage.tsx | 41 +++++++++++++++++-----
1 file changed, 33 insertions(+), 8 deletions(-)
diff --git a/src/components/character/CharacterPage.tsx b/src/components/character/CharacterPage.tsx
index 1711b3f..09cec46 100644
--- a/src/components/character/CharacterPage.tsx
+++ b/src/components/character/CharacterPage.tsx
@@ -168,14 +168,39 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
{/* 콘텐츠 영역 */}
{isSyncing || isLoading ? (
-
-
-
-
- 캐릭터 정보를 불러오는 중...
-
-
-
+
+ {showStats ? (
+
+ {/* 캐릭터 정보와 어빌리티 스켈레톤 */}
+
+
+ {/* 기본 스탯 스켈레톤 */}
+
+
+ {/* 하이퍼 스탯 스켈레톤 */}
+
+
+ ) : (
+
+ {/* 캐릭터 이미지 스켈레톤 */}
+
+ {/* 장비 정보 스켈레톤 */}
+
+ {Array.from({ length: 15 }).map((_, index) => (
+
+ ))}
+
+
+ )}
) : (
From 6170a7a2c8ed8b67f463e331a0d01cc6f767882e Mon Sep 17 00:00:00 2001
From: echo
Date: Wed, 25 Jun 2025 01:37:39 +0900
Subject: [PATCH 05/10] =?UTF-8?q?feat=20:=20=EC=BA=90=EB=A6=AD=ED=84=B0=20?=
=?UTF-8?q?=EB=8F=99=EA=B8=B0=ED=99=94=20=EC=84=B1=EA=B3=B5/=EC=8B=A4?=
=?UTF-8?q?=ED=8C=A8=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/character/CharacterPage.tsx | 4 +---
src/hooks/character/useCharacterData.ts | 15 +++++++++++++--
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/src/components/character/CharacterPage.tsx b/src/components/character/CharacterPage.tsx
index 09cec46..7251504 100644
--- a/src/components/character/CharacterPage.tsx
+++ b/src/components/character/CharacterPage.tsx
@@ -63,7 +63,7 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
- {type === 'search' ? (
+ {type === 'character' && (
- ) : (
-
)}
diff --git a/src/hooks/character/useCharacterData.ts b/src/hooks/character/useCharacterData.ts
index a2750c3..bc9a673 100644
--- a/src/hooks/character/useCharacterData.ts
+++ b/src/hooks/character/useCharacterData.ts
@@ -15,10 +15,13 @@ import {
import { useUserStore } from '../../store/userStore'
import { useEffect } from 'react'
import { syncCharacter } from '../../apis/user/userController'
+import { useAuth } from '../useAuth'
+import { useNavigate } from 'react-router-dom'
export const useCharacterData = () => {
const { setUserName, characterOcid } = useUserStore()
-
+ const { userLogout } = useAuth()
+ const nav = useNavigate()
const {
data: characterStats,
isLoading: statsLoading,
@@ -72,7 +75,15 @@ export const useCharacterData = () => {
})
const mutateSyncCharacter = useMutation({
- mutationFn: () => syncCharacter()
+ mutationFn: () => syncCharacter(),
+ onSuccess: () => {
+ alert('동기화가 완료되었습니다. 다시 로그인해주세요.')
+ userLogout()
+ nav('/')
+ },
+ onError: () => {
+ alert('동기화에 실패했습니다. 다시 시도해주세요.')
+ }
})
const syncCharacterHandler = async () => {
From 780679713bc1af40359bf7d801f1dd0b4d590ad9 Mon Sep 17 00:00:00 2001
From: echo
Date: Wed, 25 Jun 2025 01:50:09 +0900
Subject: [PATCH 06/10] =?UTF-8?q?feat=20:=20=EC=97=90=EB=9F=AC=EC=8B=9C=20?=
=?UTF-8?q?=EB=8B=A4=EB=A5=B8=20=EC=BA=90=EB=A6=AD=ED=84=B0=20=EA=B2=80?=
=?UTF-8?q?=EC=83=89=20=EA=B0=80=EB=8A=A5=ED=95=98=EA=B2=8C=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/character/CharacterPage.tsx | 96 +++++++++++++++-------
1 file changed, 67 insertions(+), 29 deletions(-)
diff --git a/src/components/character/CharacterPage.tsx b/src/components/character/CharacterPage.tsx
index 7251504..c125abe 100644
--- a/src/components/character/CharacterPage.tsx
+++ b/src/components/character/CharacterPage.tsx
@@ -10,7 +10,8 @@ import { useInventory } from '../../hooks/character/useInventory'
import { useUserStore } from '../../store/userStore'
import { searchCharacterOcid } from '../../apis/character/characterController'
-import { FiAlertTriangle, FiRefreshCcw } from 'react-icons/fi'
+import { FiAlertTriangle, FiRefreshCcw, FiSearch } from 'react-icons/fi'
+import Button from '../common/Button'
interface CharacterPageProps {
type: 'character' | 'search'
@@ -62,37 +63,74 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
return (
-
- {type === 'character' && (
-
-
+
+
+ {type === 'character' && (
+
+
+
+ )}
+
+
+
+
+
+ 캐릭터 정보를 불러올 수 없습니다
+
+
+ 2023년 12월 21일 이후의 데이터만 조회할 수 있습니다.
+
+
+
+
+
+
+ setCharacterName(e.target.value)}
+ className="w-full px-4 py-2.5 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm bg-white"
+ />
+
- )}
-
-
-
- 캐릭터 정보를 불러올 수 없습니다
-
-
- 2023년 12월 21일 이후의 데이터만 조회할 수 있습니다.
-
)
From 05ac4a0efe72344ac04cbc6d640fcbc9a3986ae7 Mon Sep 17 00:00:00 2001
From: echo
Date: Wed, 25 Jun 2025 02:12:15 +0900
Subject: [PATCH 07/10] =?UTF-8?q?feat=20:=20=ED=95=B4=EB=8D=94=20=EC=BA=90?=
=?UTF-8?q?=EB=A6=AD=ED=84=B0=EC=A0=95=EB=B3=B4=20=ED=81=B4=EB=A6=AD?=
=?UTF-8?q?=EC=8B=9C=20=EA=B8=B0=EC=A1=B4=20ocid=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/Header.tsx | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/components/common/Header.tsx b/src/components/common/Header.tsx
index 8a00f9f..6d4f307 100644
--- a/src/components/common/Header.tsx
+++ b/src/components/common/Header.tsx
@@ -5,6 +5,7 @@ import Logo from '../../assets/logo.png'
import { useState } from 'react'
import { useAuthStore } from '../../store/authStore'
import KakaoOpenChatButton from './KakaoOpenChatButton'
+import { useUserStore } from '../../store/userStore'
function Header() {
const { userLogout } = useAuth()
@@ -12,6 +13,7 @@ function Header() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
const { userType } = useAuthStore()
+ const { setCharacterOcid, userInfo } = useUserStore()
const KAKAO_CHAT_LINK = 'https://open.kakao.com/o/s4tfG2Ah'
@@ -59,7 +61,10 @@ function Header() {
From d0b01554192c47a2a47ff93c8d78739bdf3e331b Mon Sep 17 00:00:00 2001
From: echo
Date: Wed, 25 Jun 2025 17:53:01 +0900
Subject: [PATCH 08/10] =?UTF-8?q?style=20:=20=EC=BA=90=EB=A6=AD=ED=84=B0?=
=?UTF-8?q?=20=EB=A7=A4=EB=8B=88=EC=A0=80=20=EA=B4=80=EB=A0=A8=20=EB=AC=B8?=
=?UTF-8?q?=EA=B5=AC=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=A7=A4=EB=8B=88?=
=?UTF-8?q?=EC=A0=80=20=EC=B6=94=EA=B0=80=20=EB=94=94=EC=9E=90=EC=9D=B8=20?=
=?UTF-8?q?=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/character/CharacterPage.tsx | 118 +++++++---
src/components/modal/room/RoomManageModal.tsx | 201 ++++++++++++++----
2 files changed, 244 insertions(+), 75 deletions(-)
diff --git a/src/components/character/CharacterPage.tsx b/src/components/character/CharacterPage.tsx
index c125abe..5ede95d 100644
--- a/src/components/character/CharacterPage.tsx
+++ b/src/components/character/CharacterPage.tsx
@@ -67,24 +67,50 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
{type === 'character' && (
-
+
+
+
+
+
+
+
+ 본캐릭터는 넥슨 OpenAPI에서 레벨이 가장 높은
+ 캐릭터를 기준으로 자동 설정됩니다.
+
+
+
+ 정보가 정확하지 않다면
+
+ 동기화
+
+ 버튼을 눌러주세요
+
+
+
+
+
+
)}
@@ -142,20 +168,48 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
{type === 'character' ? (
-
+
+
+
+
+
+
+
+ 본캐릭터는 넥슨 OpenAPI에서 레벨이 가장 높은 캐릭터를
+ 기준으로 자동 설정됩니다.
+
+
+
+ 정보가 정확하지 않다면
+
+ 동기화
+ 버튼을 눌러주세요
+
+
+
+
+
) : (
diff --git a/src/components/modal/room/RoomManageModal.tsx b/src/components/modal/room/RoomManageModal.tsx
index 2c9388a..f58448d 100644
--- a/src/components/modal/room/RoomManageModal.tsx
+++ b/src/components/modal/room/RoomManageModal.tsx
@@ -1,5 +1,4 @@
import { Room } from '../../../types/rooms'
-import InputText from '../../common/InputText'
import ModalLayout from '../ModalLayout'
import { useAdmin } from '../../../hooks/room/useAdmin'
import { useState } from 'react'
@@ -76,80 +75,196 @@ export const GuildManageModal = ({
size="medium"
title={`${room.groupName} 관리`}
showFooterButtons={false}>
-
-
-
-
관리자 목록
-
-
{
- setCharacterName(e.target.value)
- // 입력 시작하면 이전 메시지 제거
- setMessage(null)
- }}
- />
+
+
+ {/* 안내 메시지 */}
+
+
+
+
+
+
+
+ 관리자 초대 시 주의사항
+
+
+ 관리자로 초대할 때는 반드시 해당 길드원의{' '}
+ 본캐릭터를 등록해야 합니다.
+
+ 본캐릭터는 넥슨 OpenAPI에서 레벨이 가장 높은 캐릭터를 기준으로
+ 자동 설정됩니다.
+
+
+
+
+
+ {/* 관리자 추가 폼 */}
+
+
+
+ 관리자 추가
+
+
+
+
+
+
{
+ setCharacterName(e.target.value)
+ setMessage(null)
+ }}
+ className="w-full pl-10 pr-4 py-2.5 bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
+ />
+
+ {/* 메시지 표시 */}
{message && (
- {message.text}
+ {message.text}
)}
-
-
-
닉네임
-
권한
+ {/* 관리자 목록 */}
+
+
+
+ 관리자 목록
+
- {room.admins.map((admin, index) => (
-
-
{admin}
-
- {room.admins.length > 0 &&
- room.admins[index] !== userName && (
+
+
+ {room.admins.map(admin => (
+
+
+
+
+
+ {admin}
+
+ {admin === userName && (
+
+ 나
+
+ )}
+
+
+ {admin !== userName && (
)}
-
+
+ ))}
- ))}
+
From bbdfe1592e0c6c62d15677be2f1765dd400cfba7 Mon Sep 17 00:00:00 2001
From: echo
Date: Fri, 27 Jun 2025 09:48:23 +0900
Subject: [PATCH 09/10] =?UTF-8?q?fix=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?=
=?UTF-8?q?=EC=97=90=EB=9F=AC=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/character/CharacterPage.tsx | 3 +--
src/components/modal/room/RoomManageModal.tsx | 6 +++---
src/hooks/character/useCharacterData.ts | 4 ++--
src/hooks/useAuth.ts | 20 ++++++++++++-------
src/pages/Home.tsx | 6 ++----
5 files changed, 21 insertions(+), 18 deletions(-)
diff --git a/src/components/character/CharacterPage.tsx b/src/components/character/CharacterPage.tsx
index 5ede95d..40ffdb5 100644
--- a/src/components/character/CharacterPage.tsx
+++ b/src/components/character/CharacterPage.tsx
@@ -51,8 +51,7 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
return
}
setCharacterOcid(ocid)
- } catch (error) {
- console.error(error)
+ } catch {
alert('캐릭터 검색에 실패했습니다.')
} finally {
setSearchLoading(false)
diff --git a/src/components/modal/room/RoomManageModal.tsx b/src/components/modal/room/RoomManageModal.tsx
index f58448d..625b755 100644
--- a/src/components/modal/room/RoomManageModal.tsx
+++ b/src/components/modal/room/RoomManageModal.tsx
@@ -76,7 +76,7 @@ export const GuildManageModal = ({
title={`${room.groupName} 관리`}
showFooterButtons={false}>
-
+
{/* 안내 메시지 */}
@@ -184,13 +184,13 @@ export const GuildManageModal = ({
)}
{/* 관리자 목록 */}
-
+
관리자 목록
-
+
{room.admins.map(admin => (
{
try {
await mutateSyncCharacter.mutateAsync()
- } catch (error) {
- console.error(error)
+ } catch {
+ alert('동기화에 실패했습니다. 다시 시도해주세요.')
}
}
diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts
index c47e7bb..7dd1b7c 100644
--- a/src/hooks/useAuth.ts
+++ b/src/hooks/useAuth.ts
@@ -10,17 +10,23 @@ export const useAuth = () => {
const userLogin = async () => {
try {
- const provider = new GoogleAuthProvider()
+ const provider = await new GoogleAuthProvider()
const result = await signInWithPopup(authService, provider)
const token = await result.user.getIdToken()
- if (token && result.user.uid) {
- storeLogin(token, result.user.uid, 'member')
- const userInfo = await fetchUserInfo(result.user.uid)
- return userInfo
+ if (!token || !result.user.uid) {
+ throw new Error('로그인에 필요한 정보를 가져올 수 없습니다.')
}
- } catch (error) {
- console.log(error)
+
+ storeLogin(token, result.user.uid, 'member')
+ const userInfo = await fetchUserInfo(result.user.uid)
+
+ if (!userInfo) {
+ throw new Error('사용자 정보를 가져올 수 없습니다.')
+ }
+ return userInfo
+ } catch {
+ return null
}
}
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
index a38ae8f..0a2c66c 100644
--- a/src/pages/Home.tsx
+++ b/src/pages/Home.tsx
@@ -60,8 +60,7 @@ const Home = () => {
nav('/signup')
}
}
- } catch (error) {
- console.error(error)
+ } catch {
alert('로그인에 실패했습니다.')
}
}
@@ -83,8 +82,7 @@ const Home = () => {
await storeLogin('', '', 'search')
setCharacterOcid(ocid)
nav(`/searchCharacter`)
- } catch (error) {
- console.error(error)
+ } catch {
alert('캐릭터 검색에 실패했습니다.')
} finally {
setSearchLoading(false)
From b9ac9361bacfe62c862f72c381e5d1c5c8f9427d Mon Sep 17 00:00:00 2001
From: echo
Date: Fri, 27 Jun 2025 12:14:49 +0900
Subject: [PATCH 10/10] =?UTF-8?q?remove=20:=20=EC=9E=84=EC=8B=9C=20?=
=?UTF-8?q?=EC=BA=90=EB=A6=AD=ED=84=B0=20=EB=8F=99=EA=B8=B0=ED=99=94=20?=
=?UTF-8?q?=EC=82=AD=EC=A0=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/character/CharacterPage.tsx | 24 ++++++++--------------
1 file changed, 8 insertions(+), 16 deletions(-)
diff --git a/src/components/character/CharacterPage.tsx b/src/components/character/CharacterPage.tsx
index 40ffdb5..66e8d89 100644
--- a/src/components/character/CharacterPage.tsx
+++ b/src/components/character/CharacterPage.tsx
@@ -10,7 +10,7 @@ import { useInventory } from '../../hooks/character/useInventory'
import { useUserStore } from '../../store/userStore'
import { searchCharacterOcid } from '../../apis/character/characterController'
-import { FiAlertTriangle, FiRefreshCcw, FiSearch } from 'react-icons/fi'
+import { FiAlertTriangle, FiSearch } from 'react-icons/fi'
import Button from '../common/Button'
interface CharacterPageProps {
@@ -18,16 +18,8 @@ interface CharacterPageProps {
}
export const CharacterPage = ({ type }: CharacterPageProps) => {
- const {
- characterStats,
- ability,
- hyperStat,
- basic,
- isLoading,
- error,
- syncCharacterHandler,
- isSyncing
- } = useCharacterData()
+ const { characterStats, ability, hyperStat, basic, isLoading, error } =
+ useCharacterData()
const [characterName, setCharacterName] = useState('')
const { inventory } = useInventory()
@@ -67,7 +59,7 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
{type === 'character' && (
-
+ */}
@@ -168,7 +160,7 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
{type === 'character' ? (
-
+ */}
@@ -256,7 +248,7 @@ export const CharacterPage = ({ type }: CharacterPageProps) => {
{/* 콘텐츠 영역 */}
- {isSyncing || isLoading ? (
+ {isLoading ? (