모바일 새로고침 시 좌측 사이드바 열림→닫힘 애니메이션 문제 수정

- useMenuState에 menuMounted 추가, 모바일은 항상 닫힘 상태로 시작
- 좌측 사이드바 모바일 오버레이·백드롭을 마운트 이후에만 열리도록 게이팅
- 데스크톱 기본 열림·저장 설정은 그대로 유지
- v1.10.30

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-03 17:45:39 +09:00
parent b669d34e83
commit bb8d20f138
8 changed files with 45 additions and 16 deletions

View File

@@ -5,6 +5,10 @@ defineProps({
menuOpen: {
type: Boolean,
required: true
},
menuMounted: {
type: Boolean,
default: false
}
})
@@ -229,9 +233,14 @@ onMounted(() => {
<aside
id="menu"
class="left-sidebar site-sidebar flex flex-col overflow-hidden border-r border-[var(--site-line)] transition-[width,opacity,transform,border-color] duration-300 ease-out max-lg:fixed max-lg:left-0 max-lg:top-[57px] max-lg:z-[60] max-lg:h-[calc(100dvh-57px)] max-lg:max-h-[calc(100dvh-57px)] max-lg:w-[min(287px,calc(100vw-24px))] max-lg:shadow-[0_16px_48px_rgba(0,0,0,0.18)] lg:sticky lg:top-[57px] lg:z-10 lg:h-[calc(100vh-57px)] lg:max-h-[calc(100vh-57px)] lg:self-start"
:class="menuOpen
? 'max-lg:translate-x-0 max-lg:pointer-events-auto lg:w-[287px] lg:opacity-100'
: 'max-lg:-translate-x-full max-lg:pointer-events-none lg:w-0 lg:opacity-0 lg:border-transparent'"
:class="[
menuOpen
? 'lg:w-[287px] lg:opacity-100'
: 'lg:w-0 lg:opacity-0 lg:border-transparent',
menuOpen && menuMounted
? 'max-lg:translate-x-0 max-lg:pointer-events-auto'
: 'max-lg:-translate-x-full max-lg:pointer-events-none'
]"
>
<div class="left-sidebar__scroll site-sidebar-scroll min-h-0 flex-1">
<div class="left-sidebar__block site-sidebar-section py-3 pl-4 pr-3 sm:pl-5 xl:pl-0">

View File

@@ -6,13 +6,26 @@ const menuStorageKey = 'MENU_STATE'
*/
export const useMenuState = () => {
const menuOpen = useState('site-menu-open', () => true)
const menuMounted = useState('site-menu-mounted', () => false)
onMounted(() => {
const savedState = localStorage.getItem(menuStorageKey)
if (menuMounted.value) {
return
}
const isNarrow = window.matchMedia('(max-width: 1023px)').matches
if (isNarrow) {
// 모바일은 항상 닫힌 상태로 시작한다(데스크톱 저장 설정은 보존).
menuOpen.value = false
} else {
const savedState = localStorage.getItem(menuStorageKey)
if (savedState) {
menuOpen.value = savedState === 'open'
}
}
menuMounted.value = true
})
/**
@@ -38,6 +51,7 @@ export const useMenuState = () => {
return {
menuOpen,
menuMounted,
toggleMenu,
closeMenu
}

View File

@@ -412,7 +412,7 @@
| nuxt.config.js | Nuxt 앱 설정, `tailwindcss.cssPath`로 `main.css` 단일 엔트리, Tailwind 모듈, 관리자 QA를 위한 개발 도구 비활성화, 회원 썸네일 최소/최대 해상도·품질 런타임 설정 |
| tailwind.config.js | Tailwind 테마 설정 |
| assets/css/main.css | 전역 스타일, 공개 배경·사이드바 배경 통일 기준, 관리자 라이트 테마 격리(`admin-layout`), 좌측 사이드바/그리드 전환 애니메이션, 네비게이션 세로 바 hover 효과 |
| composables/useMenuState.js | 좌측 메뉴 열림 상태·`closeMenu`(모바일 백드롭 등) |
| composables/useMenuState.js | 좌측 메뉴 열림 상태·`closeMenu`(모바일 백드롭 등), 모바일은 마운트 시 항상 닫힘 시작·`menuMounted`로 첫 페인트 슬라이드 플래시 방지 |
| composables/useThemeMode.js | 사용자 화면 라이트/다크 테마 상태 관리 |
| middleware/admin-auth.global.js | 관리자 페이지 접근 인증 |
| scripts/dev-server.js | 로컬 개발 서버 링크 요약 출력 |

View File

@@ -1,5 +1,11 @@
# 업데이트 이력
## v1.10.30
- 모바일에서 새로고침 시 좌측 사이드바가 열렸다 닫히는 애니메이션 문제 수정.
- `useMenuState`에 `menuMounted` 상태 추가, 모바일은 항상 닫힌 상태로 시작(데스크톱 저장 설정은 보존)하도록 수정.
- 좌측 사이드바 모바일 슬라이드 오버레이·백드롭이 마운트 이후에만 열리도록 게이팅해 첫 페인트 플래시·슬라이드 재생 제거.
## v1.10.29
- 한글 IME 조합 중(`compositionupdate`)에도 검색이 즉시 반영되는 공통 컴포저블 `useImeSearchInput` 추가(NFC 정규화·선택적 디바운스).

View File

@@ -1,5 +1,5 @@
<script setup>
const { menuOpen, closeMenu } = useMenuState()
const { menuOpen, menuMounted, closeMenu } = useMenuState()
/**
* 모바일에서 좌측 슬라이드 메뉴가 열려 있을 때 문서 스크롤을 잠근다.
@@ -40,7 +40,7 @@ onBeforeUnmount(() => {
leave-to-class="opacity-0"
>
<div
v-show="menuOpen"
v-show="menuOpen && menuMounted"
class="public-layout__nav-backdrop fixed inset-x-0 bottom-0 z-40 bg-black/35 backdrop-blur-[2px] lg:hidden"
style="top: var(--site-top-chrome-height, 57px)"
aria-hidden="true"
@@ -60,7 +60,7 @@ onBeforeUnmount(() => {
<div class="public-layout__right-column lg:col-start-3 lg:row-start-1 lg:self-stretch lg:border-l lg:border-[var(--site-line)]">
<RightSidebar />
</div>
<LeftSidebar :menu-open="menuOpen" class="lg:col-start-1 lg:row-start-1" />
<LeftSidebar :menu-open="menuOpen" :menu-mounted="menuMounted" class="lg:col-start-1 lg:row-start-1" />
</div>
</div>
</template>

View File

@@ -1,5 +1,5 @@
<script setup>
const { menuOpen, closeMenu } = useMenuState()
const { menuOpen, menuMounted, closeMenu } = useMenuState()
/**
* 모바일에서 좌측 슬라이드 메뉴가 열려 있을 때 문서 스크롤을 잠근다.
@@ -40,7 +40,7 @@ onBeforeUnmount(() => {
leave-to-class="opacity-0"
>
<div
v-show="menuOpen"
v-show="menuOpen && menuMounted"
class="post-layout__nav-backdrop fixed inset-x-0 bottom-0 z-40 bg-black/35 backdrop-blur-[2px] lg:hidden"
style="top: var(--site-top-chrome-height, 57px)"
aria-hidden="true"
@@ -60,7 +60,7 @@ onBeforeUnmount(() => {
<div class="post-layout__right-column lg:col-start-3 lg:row-start-1 lg:self-stretch lg:border-l lg:border-[var(--site-line)]">
<RightSidebar />
</div>
<LeftSidebar :menu-open="menuOpen" class="lg:col-start-1 lg:row-start-1" />
<LeftSidebar :menu-open="menuOpen" :menu-mounted="menuMounted" class="lg:col-start-1 lg:row-start-1" />
</div>
</div>
</template>

View File

@@ -1,6 +1,6 @@
{
"name": "sori.studio",
"version": "1.10.29",
"version": "1.10.30",
"private": true,
"type": "module",
"imports": {

View File

@@ -321,7 +321,7 @@ const goPreviousStep = () => {
{{ errors.emailOtp }}
</p>
<p class="text-xs text-[#8c95a3]">
Resend로 발송됩니다. 메일이 보이지 않으면 스팸함을 확인해 주세요.
메일이 보이지 않으면 스팸함을 확인해 주세요.
</p>
</div>