From 52fe72401619bb89acf75a327049246fb99e20ef Mon Sep 17 00:00:00 2001 From: echo Date: Wed, 10 Sep 2025 22:50:41 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[refactor]=20=EA=B7=B8=EB=A3=B9=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/room/EmptyRoomState.tsx | 7 ++ src/components/room/RoomCard.tsx | 59 +++++++++++++ src/components/room/RoomGrid.tsx | 30 +++++++ src/components/room/RoomListHeader.tsx | 29 +++++++ src/pages/RoomList.tsx | 111 +++++++------------------ 5 files changed, 157 insertions(+), 79 deletions(-) create mode 100644 src/components/room/EmptyRoomState.tsx create mode 100644 src/components/room/RoomCard.tsx create mode 100644 src/components/room/RoomGrid.tsx create mode 100644 src/components/room/RoomListHeader.tsx diff --git a/src/components/room/EmptyRoomState.tsx b/src/components/room/EmptyRoomState.tsx new file mode 100644 index 0000000..ab1890c --- /dev/null +++ b/src/components/room/EmptyRoomState.tsx @@ -0,0 +1,7 @@ +export const EmptyRoomState = () => { + return ( +
+

관리방이 없습니다.

+
+ ) +} diff --git a/src/components/room/RoomCard.tsx b/src/components/room/RoomCard.tsx new file mode 100644 index 0000000..c19f784 --- /dev/null +++ b/src/components/room/RoomCard.tsx @@ -0,0 +1,59 @@ +import { IoTrashOutline, IoPersonAddOutline } from 'react-icons/io5' +import { Room } from '../../types/rooms' + +interface Props { + room: Room + onEnterRoom: (room: Room) => void + onManageRoom: (room: Room) => void + onDeleteRoom: (adminId: string) => void +} + +export const RoomCard = ({ + room, + onEnterRoom, + onManageRoom, + onDeleteRoom +}: Props) => { + return ( +
+
+
+
+

+ {room.groupName} +

+
+
+ +
+
+

메인 길드

+

+ {room.mainGuild.name} +

+
+
+ +
+ + + +
+
+
+ ) +} diff --git a/src/components/room/RoomGrid.tsx b/src/components/room/RoomGrid.tsx new file mode 100644 index 0000000..984421c --- /dev/null +++ b/src/components/room/RoomGrid.tsx @@ -0,0 +1,30 @@ +import { Room } from '../../types/rooms' +import { RoomCard } from './RoomCard' + +interface Props { + rooms: Room[] + onEnterRoom: (room: Room) => void + onManageRoom: (room: Room) => void + onDeleteRoom: (adminId: string) => void +} + +export const RoomGrid = ({ + rooms, + onEnterRoom, + onManageRoom, + onDeleteRoom +}: Props) => { + return ( +
+ {rooms.map(room => ( + + ))} +
+ ) +} diff --git a/src/components/room/RoomListHeader.tsx b/src/components/room/RoomListHeader.tsx new file mode 100644 index 0000000..6e6caf9 --- /dev/null +++ b/src/components/room/RoomListHeader.tsx @@ -0,0 +1,29 @@ +import { IoAdd } from 'react-icons/io5' + +interface Props { + onCreateRoom: () => void +} + +export const RoomListHeader = ({ onCreateRoom }: Props) => { + return ( +
+
+

길드 관리 홈

+

관리중인 길드방 목록입니다

+
+
+ +
+
+ ⚠️ 관리방 생성은 길드 마스터만 가능합니다. +
마스터가 아니라면, 해당 마스터가 생성후 그룹에 초대해 주세요. +
+
+
+
+ ) +} diff --git a/src/pages/RoomList.tsx b/src/pages/RoomList.tsx index fe2abbc..fb2c4db 100644 --- a/src/pages/RoomList.tsx +++ b/src/pages/RoomList.tsx @@ -1,7 +1,6 @@ import { useModalStore } from '../store/modalStore' import { CreateRoomModal } from '../components/modal/room/CreateRoomModal' import { useNavigate } from 'react-router-dom' -import { IoAdd, IoTrashOutline, IoPersonAddOutline } from 'react-icons/io5' import { useRoom } from '../hooks/room/useRoom' import { Loading } from '../components/common/Loading' import { GuildManageModal } from '../components/modal/room/RoomManageModal' @@ -9,6 +8,9 @@ import { useState } from 'react' import { Room } from '../types/rooms' import { useRoomsStore } from '../store/roomsStore' import { useUserStore } from '../store/userStore' +import { RoomListHeader } from '../components/room/RoomListHeader' +import { RoomGrid } from '../components/room/RoomGrid' +import { EmptyRoomState } from '../components/room/EmptyRoomState' export const RoomList = () => { const { openModal, activeModal } = useModalStore() @@ -19,6 +21,24 @@ export const RoomList = () => { const [selectedRoom, setSelectedRoom] = useState(null) + const handleCreateRoom = () => { + openModal('createRoom') + } + + const handleEnterRoom = (room: Room) => { + navigate(`/room/${room.groupName}`) + setGroupId(room.adminId) + } + + const handleManageRoom = (room: Room) => { + setSelectedRoom(room) + openModal('guildManage') + } + + const handleDeleteRoom = (adminId: string) => { + deleteRoomHandler(Number(adminId)) + } + if (!rooms) { return (
@@ -32,86 +52,19 @@ export const RoomList = () => { return (
-
-
-

길드 관리 홈

-

관리중인 길드방 목록입니다

-
-
- -
-
- ⚠️ 관리방 생성은 길드 마스터만 가능합니다. -
마스터가 아니라면, 해당 마스터가 생성후 그룹에 초대해 - 주세요. -
-
-
-
- {rooms.length === 0 && ( -
-

관리방이 없습니다.

-
- )} - -
- {rooms.map(room => ( -
-
-
-
-

- {room.groupName} -

-
-
+ -
-
-

메인 길드

-

- {room.mainGuild.name} -

-
-
+ {rooms.length === 0 ? ( + + ) : ( + + )} -
- - - -
-
-
- ))} -
{activeModal === 'createRoom' && } {activeModal === 'guildManage' && userName && ( Date: Sun, 14 Sep 2025 15:57:23 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[Refactor]=20=EA=B7=B8=EB=A3=B9=20=EA=B8=B8?= =?UTF-8?q?=EB=93=9C=20=EA=B4=80=EB=A6=AC=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/room/RoomActionBar.tsx | 32 ++++++++++ src/components/room/RoomContent.tsx | 57 ++++++++++++++++++ src/components/room/RoomHeader.tsx | 22 +++++++ src/pages/Room.tsx | 84 ++++++++------------------- 4 files changed, 136 insertions(+), 59 deletions(-) create mode 100644 src/components/room/RoomActionBar.tsx create mode 100644 src/components/room/RoomContent.tsx create mode 100644 src/components/room/RoomHeader.tsx diff --git a/src/components/room/RoomActionBar.tsx b/src/components/room/RoomActionBar.tsx new file mode 100644 index 0000000..90446e5 --- /dev/null +++ b/src/components/room/RoomActionBar.tsx @@ -0,0 +1,32 @@ +import { ModalType } from '../../store/modalStore' +import { ActionBtnList } from '../guild/ActionBtnList' +import { ListSwitch } from '../guild/ListSwitch' +import { Guild } from '../../types/guild' + +interface Props { + showModal: (name: ModalType) => void + guildList: Guild[] + handleDetect: () => void + refreshMember: (guildId: number) => void +} + +export const RoomActionBar = ({ + showModal, + guildList, + handleDetect, + refreshMember +}: Props) => { + return ( +
+
+ + {guildList.length > 0 && } +
+
+ ) +} diff --git a/src/components/room/RoomContent.tsx b/src/components/room/RoomContent.tsx new file mode 100644 index 0000000..ecd1053 --- /dev/null +++ b/src/components/room/RoomContent.tsx @@ -0,0 +1,57 @@ +import { Guild, Member, NexonMembers } from '../../types/guild' +import { MemberContainer } from '../guild/MemberContainer' +import { Empty } from '../common/Empty' + +interface Props { + nexonMembersLoading: boolean + guildList: Guild[] + selectMember: NexonMembers + nexonMembers: NexonMembers[] | null + main: string | undefined + searchCharacter: string + onMemberSelect: (type: string, member: Member) => void + onSearchCharacter: (value: string) => void + onDeleteGuild?: () => void +} + +export const RoomContent = ({ + nexonMembersLoading, + guildList, + selectMember, + nexonMembers, + main, + searchCharacter, + onMemberSelect, + onSearchCharacter, + onDeleteGuild +}: Props) => { + return ( +
+
+ {nexonMembersLoading && ( +
+
+

+ 캐릭터 정보를 불러오는 중... +

+
+ )} + {guildList.length > 0 ? ( + + ) : ( + + )} +
+
+ ) +} diff --git a/src/components/room/RoomHeader.tsx b/src/components/room/RoomHeader.tsx new file mode 100644 index 0000000..c72a732 --- /dev/null +++ b/src/components/room/RoomHeader.tsx @@ -0,0 +1,22 @@ +import { IoArrowBack } from 'react-icons/io5' + +interface Props { + onBack: () => void +} + +export const RoomHeader = ({ onBack }: Props) => { + return ( +
+ +
+

길드 관리

+

길드원 정보 관리

+
+
+ ) +} diff --git a/src/pages/Room.tsx b/src/pages/Room.tsx index b313165..4b0bba8 100644 --- a/src/pages/Room.tsx +++ b/src/pages/Room.tsx @@ -1,21 +1,19 @@ import { CreateGuildModal } from '../components/modal/guild/CreateGuildModal' import { ModalType, useModalStore } from '../store/modalStore' import { useGuildsList } from '../hooks/guild/useGuildsList' -import { MemberContainer } from '../components/guild/MemberContainer' -import { ListSwitch } from '../components/guild/ListSwitch' -import { ActionBtnList } from '../components/guild/ActionBtnList' import { DetectMemberModal } from '../components/modal/guild/DetectMemberModal' import { useGuildMember } from '../hooks/guild/useGuildMember' -import { Empty } from '../components/common/Empty' import { DetailMemberModal } from '../components/modal/guild/DetailMemberModal' import { Loading } from '../components/common/Loading' import { useState } from 'react' import { Member, NexonMembers } from '../types/guild' -import { IoArrowBack } from 'react-icons/io5' import { useNavigate } from 'react-router-dom' import { useGuildDetect } from '../hooks/guild/useGuildDetect' import { findMainCharacter } from '../apis/character/characterController' import { AlertModal } from '../components/modal/common/AlertModal' +import { RoomHeader } from '../components/room/RoomHeader' +import { RoomActionBar } from '../components/room/RoomActionBar' +import { RoomContent } from '../components/room/RoomContent' const Room = () => { const { activeModal, openModal } = useModalStore() @@ -96,63 +94,31 @@ const Room = () => { return (
-
- -
-

길드 관리

-

길드원 정보 관리

-
-
+ navigate('/rooms')} />
-
-
- - {guildList.length > 0 && } -
-
+ void} + /> -
-
- {nexonMembersLoading && ( -
-
-

- 캐릭터 정보를 불러오는 중... -

-
- )} - {guildList.length > 0 ? ( - deleteGuild(selectMember.guildId as number) - : undefined - } - /> - ) : ( - - )} -
-
+ deleteGuild(selectMember.guildId as number) + : undefined + } + />
From 3782d57c6adb24f7933278095a7b32b11150351f Mon Sep 17 00:00:00 2001 From: echo Date: Sun, 14 Sep 2025 16:09:06 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[Fix]=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EB=B0=98=EB=B3=B5=20=EC=8B=A4=ED=96=89=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/layout/ProtectedRoute.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/layout/ProtectedRoute.tsx b/src/components/layout/ProtectedRoute.tsx index d7e76fa..b9f016f 100644 --- a/src/components/layout/ProtectedRoute.tsx +++ b/src/components/layout/ProtectedRoute.tsx @@ -2,6 +2,8 @@ import { Navigate, useLocation } from 'react-router-dom' import { useAuthStore } from '../../store/authStore' import { useUserStore } from '../../store/userStore' import { useAuth } from '../../hooks/useAuth' +import { useEffect } from 'react' + interface ProtectedRouteProps { children: React.ReactNode } @@ -14,9 +16,16 @@ export const ProtectedRoute = ({ children }: ProtectedRouteProps) => { const path = location.pathname + // useEffect로 사이드 이펙트 처리 + useEffect(() => { + // 1. 로그인도 안 됐는데 루트가 아닌 경로 접근 시 + if (!isLoggedIn && path !== '/') { + userLogout() + } + }, [isLoggedIn, path, userLogout]) + // 1. 로그인도 안 됐는데 루트가 아닌 경로 접근 시 if (!isLoggedIn && path !== '/') { - userLogout() return } From 77b244bf1c82cb454f3553d518c4b1a469c25ea1 Mon Sep 17 00:00:00 2001 From: echo Date: Sun, 14 Sep 2025 16:26:35 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[Refactor]=20=EA=B8=B8=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20=EB=AA=A8=EB=B0=94?= =?UTF-8?q?=EC=9D=BC=20=EB=B2=84=EC=A0=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/guild/MemberContainer.tsx | 182 +++++++++++------------ src/components/room/RoomActionBar.tsx | 24 +-- src/components/room/RoomContent.tsx | 14 +- src/components/room/RoomHeader.tsx | 16 +- 4 files changed, 116 insertions(+), 120 deletions(-) diff --git a/src/components/guild/MemberContainer.tsx b/src/components/guild/MemberContainer.tsx index c3bfae7..f1a1330 100644 --- a/src/components/guild/MemberContainer.tsx +++ b/src/components/guild/MemberContainer.tsx @@ -33,16 +33,11 @@ export const MemberContainer = ({ setSearchCharacter }: MemberContainerProps) => { const [showMenu, setShowMenu] = useState(false) - const [isOpen, setIsOpen] = useState(false) - const [sortType, setSortType] = useState('캐릭터 정렬') const [sortTypeOpen, setSortTypeOpen] = useState(false) - const [selectedType, setSelectedType] = useState('캐릭터 분류') - const [gridSize, setGridSize] = useState(2) - const [showPart, setShowPart] = useState(false) // 분류해서 보기 const dropdownRef = useRef(null) @@ -125,17 +120,18 @@ export const MemberContainer = ({ }) return ( -
+
{guildName && ( -
-
+
+
+ {/* 길드 제목 부분 */}
-

+

길드: {guildName}

-
- -
+
+ +
본캐 @@ -164,15 +160,16 @@ export const MemberContainer = ({
-
-

- 총 인원 : {members.length} + +

+

+ 총 인원: {members.length}

-

- 본캐 : {members.filter(member => member.type === '본캐').length} +

+ 본캐: {members.filter(member => member.type === '본캐').length}

-

- 부캐 : +

+ 부캐: { members.filter( member => @@ -185,8 +182,8 @@ export const MemberContainer = ({ ).length }

-

- 외부 부캐 : +

+ 외부 부캐: { members.filter( member => @@ -201,21 +198,23 @@ export const MemberContainer = ({

+ + {/* 삭제 버튼 */} {onDeleteGuild && !isMainGuild && ( -
+
{showMenu && ( -
+
@@ -225,42 +224,45 @@ export const MemberContainer = ({
)} -
-
+
+ {/* 검색 및 필터 영역 */} +
+ {/* 검색창 */}
setSearchCharacter?.(e.target.value)} placeholder="캐릭터 이름으로 검색" - className="w-full pl-10 pr-4 py-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" + className="w-full pl-9 sm:pl-10 pr-4 py-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm" /> - +
-
+
+ {/* 정렬 드롭다운 */}
{sortTypeOpen && ( -
+
)}
+ + {/* 분류 드롭다운 */}
{isOpen && ( -
- - - - - +
+ {['모두 보기', '본캐', '부캐', '외부 부캐', '특이사항'].map( + type => ( + + ) + )}
)}
+ {/* 그리드 크기 변경 */}
+ + {/* 분류해서 보기 토글 */}
-
+ + {/* 멤버 목록 */} +
{showPart ? ( (filteredMembers as Member[]).map(member => (
-
+ className="flex flex-col lg:flex-row gap-4 items-start border-b border-gray-100 pb-6 sm:pb-8"> + {/* 본캐 */} +
-
-
+ {/* 부캐들 */} +
+
{member.subCharacters?.map(subChar => ( + ? 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-4' + : 'grid-cols-2 sm:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8' + }`}> {(filteredMembers as Member[]).map(member => ( { return ( -
-
- - {guildList.length > 0 && } +
+
+
+ +
+ {guildList.length > 0 && ( +
+ +
+ )}
) diff --git a/src/components/room/RoomContent.tsx b/src/components/room/RoomContent.tsx index ecd1053..9bb43bd 100644 --- a/src/components/room/RoomContent.tsx +++ b/src/components/room/RoomContent.tsx @@ -26,12 +26,12 @@ export const RoomContent = ({ onDeleteGuild }: Props) => { return ( -
-
+
+
{nexonMembersLoading && ( -
-
-

+

+
+

캐릭터 정보를 불러오는 중...

@@ -49,7 +49,9 @@ export const RoomContent = ({ onDeleteGuild={onDeleteGuild} /> ) : ( - +
+ +
)}
diff --git a/src/components/room/RoomHeader.tsx b/src/components/room/RoomHeader.tsx index c72a732..d4ede4a 100644 --- a/src/components/room/RoomHeader.tsx +++ b/src/components/room/RoomHeader.tsx @@ -6,16 +6,20 @@ interface Props { export const RoomHeader = ({ onBack }: Props) => { return ( -
+
-
-

길드 관리

-

길드원 정보 관리

+
+

+ 길드 관리 +

+

+ 길드원 정보 관리 +

)