fix: 로그아웃을 사이드바 하단 상시 노출 버튼으로 변경

기존 드롭다운 메뉴 안에 숨어 보이지 않던 로그아웃을 항상 노출되는
버튼으로 변경한다.

- 사용자 프로필 행은 표시 전용, 아래에 로그아웃 버튼 상시 노출(빨강 호버)
- 접힘 시 아이콘만 표시
- 미사용 드롭다운(menuOpen·me-menu·me-overlay) 로직/스타일 제거

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 21:40:30 +09:00
parent f4a1253526
commit 32815a0627
+66 -83
View File
@@ -35,9 +35,6 @@ const activeNav = computed<'tasks' | 'projects'>(() =>
// 현재 로그인 사용자(프로필 아바타는 UserAvatar 가 이미지/placeholder 로 표시)
const me = computed(() => authStore.user)
// 사용자 메뉴 토글
const menuOpen = ref(false)
// --- 알림(실시간 SSE) ---
const notiStore = useNotificationStore()
const {
@@ -73,7 +70,6 @@ onMounted(() => {
// 로그아웃 → 알림 스트림 해제 후 로그인 화면으로
async function onLogout() {
menuOpen.value = false
notiStore.disconnect()
await authStore.logout()
await router.push('/login')
@@ -198,14 +194,9 @@ async function onLogout() {
><path d="M15 18l-6-6 6-6" /></svg>
<span>사이드바 접기</span>
</button>
<button
class="side-me"
type="button"
<div
class="side-user"
:title="me?.name ?? '사용자'"
:aria-label="`${me?.name ?? '사용자'} 메뉴`"
aria-haspopup="menu"
:aria-expanded="menuOpen"
@click="menuOpen = !menuOpen"
>
<UserAvatar
:url="me?.avatarUrl"
@@ -215,27 +206,29 @@ async function onLogout() {
<span class="side-me-name">{{ me?.name }}</span>
<span class="side-me-email">{{ me?.email }}</span>
</span>
</button>
<div
v-if="menuOpen"
class="me-menu side-menu"
role="menu"
>
<button
class="me-action"
type="button"
role="menuitem"
@click="onLogout"
>
로그아웃
</button>
</div>
<div
v-if="menuOpen"
class="me-overlay"
@click="menuOpen = false"
/>
<button
class="logout-btn"
type="button"
title="로그아웃"
aria-label="로그아웃"
@click="onLogout"
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
<path d="M16 17l5-5-5-5" />
<path d="M21 12H9" />
</svg>
<span>로그아웃</span>
</button>
</div>
</aside>
@@ -451,7 +444,7 @@ async function onLogout() {
.shell.collapsed .nav-item span {
display: none;
}
.shell.collapsed .side-me {
.shell.collapsed .side-user {
justify-content: center;
padding-left: 0;
padding-right: 0;
@@ -459,6 +452,14 @@ async function onLogout() {
.shell.collapsed .side-me-text {
display: none;
}
.shell.collapsed .logout-btn {
justify-content: center;
padding-left: 0;
padding-right: 0;
}
.shell.collapsed .logout-btn span {
display: none;
}
.shell.collapsed .collapse-btn {
justify-content: center;
padding-left: 0;
@@ -470,11 +471,6 @@ async function onLogout() {
.shell.collapsed .collapse-btn svg {
transform: rotate(180deg);
}
/* 접힘 상태에서 사용자 메뉴는 레일 오른쪽으로 펼침 */
.shell.collapsed .side-menu {
left: 0.5rem;
right: auto;
}
.brand {
display: flex;
@@ -569,25 +565,44 @@ async function onLogout() {
flex-shrink: 0;
transition: transform 0.18s ease;
}
.side-me {
.side-user {
display: flex;
align-items: center;
gap: 0.5625rem;
width: 100%;
padding: 0.4375rem 0.5rem;
}
.side-user > :first-child {
width: 1.875rem;
height: 1.875rem;
flex-shrink: 0;
}
/* 로그아웃 — 항상 노출되는 버튼 */
.logout-btn {
display: flex;
align-items: center;
gap: 0.6875rem;
width: 100%;
margin-top: 0.25rem;
padding: 0.5rem 0.75rem;
border: none;
background: transparent;
border-radius: var(--radius);
color: var(--text-2);
cursor: pointer;
font-family: inherit;
text-align: left;
font-size: 0.8125rem;
font-weight: 500;
line-height: 1;
white-space: nowrap;
}
.side-me:hover {
background: #f1f2f4;
.logout-btn:hover {
background: var(--red-weak);
color: var(--red);
}
.side-me > :first-child {
width: 1.875rem;
height: 1.875rem;
.logout-btn svg {
width: 1.125rem;
height: 1.125rem;
flex-shrink: 0;
}
.side-me-text {
@@ -823,43 +838,6 @@ async function onLogout() {
z-index: 55;
}
/* 사용자 드롭다운(사이드바 하단 → 위로 펼침) */
.me-menu {
position: absolute;
z-index: 60;
min-width: 11rem;
background: var(--panel);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
overflow: hidden;
}
.side-menu {
left: 0.625rem;
right: 0.625rem;
bottom: calc(100% - 0.25rem);
}
.me-action {
width: 100%;
text-align: left;
padding: 0.625rem 0.875rem;
border: none;
background: transparent;
font-family: inherit;
font-size: 0.844rem;
color: var(--text);
cursor: pointer;
}
.me-action:hover {
background: #f1f2f4;
}
/* 메뉴 바깥 클릭 감지용 투명 오버레이 */
.me-overlay {
position: fixed;
inset: 0;
z-index: 55;
}
/* 햄버거(모바일 전용) / 드로어 배경 — 데스크톱에서는 숨김 */
.hamburger {
display: none;
@@ -896,7 +874,8 @@ async function onLogout() {
/* 접힘 축소 규칙 무효화(드로어는 항상 펼친 모양) */
.shell.collapsed .brand,
.shell.collapsed .nav-item,
.shell.collapsed .side-me,
.shell.collapsed .side-user,
.shell.collapsed .logout-btn,
.shell.collapsed .collapse-btn {
justify-content: flex-start;
padding-left: 0.75rem;
@@ -904,7 +883,7 @@ async function onLogout() {
}
.shell.collapsed .brand-name,
.shell.collapsed .nav-item span,
.shell.collapsed .side-me-text {
.shell.collapsed .logout-btn span {
display: inline;
}
.shell.collapsed .side-me-text {
@@ -913,6 +892,10 @@ async function onLogout() {
.shell.collapsed .brand {
padding-left: 1.125rem;
}
.shell.collapsed .side-user {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.sidebar-backdrop {
display: block;
position: fixed;