From e847ddd227596bc32598a0411ceb4a28fb367869 Mon Sep 17 00:00:00 2001 From: zenn Date: Thu, 23 Apr 2026 13:42:53 +0900 Subject: [PATCH] =?UTF-8?q?v0.1.45=20-=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9C=A0=EC=A7=80=20=EC=98=B5=EC=85=98=EA=B3=BC=20=EB=9E=9C?= =?UTF-8?q?=EB=94=A9=20=EB=AC=B8=EA=B5=AC=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HANDOFF.md | 2 ++ TODO.md | 4 ++++ src/App.vue | 15 ++++++++++----- src/components/AuthDialog.vue | 20 +++++++++++++++++++- src/lib/authClient.js | 33 ++++++++++++++++++++++----------- 5 files changed, 57 insertions(+), 17 deletions(-) diff --git a/HANDOFF.md b/HANDOFF.md index 45c7baf..444f0c5 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -225,6 +225,8 @@ - 플래너 본문 라벨은 더 이상 `bg-paper` 배경으로 선을 덮지 않는다. `라벨 + 오른쪽 선` 구조로 바꿔 화면과 인쇄에서 노란 배경이 튀지 않도록 정리했다. - 날짜에 적용되는 목표가 새로 생기면 D-DAY는 기본 표시된다. 사용자가 해당 날짜에서 직접 `D-DAY 사용`을 끈 경우에만 로컬 숨김 목록에 저장해 다시 숨긴다. - 비로그인 랜딩은 모바일에서 `카드 안 카드`처럼 보이지 않도록 기능 설명 카드를 얇은 리스트로 단순화했고, `LOGIN` / `SIGN UP` 버튼은 같은 너비와 높이로 맞췄다. 로그인/회원가입 모달도 하단 전환 영역을 별도 카드 대신 구분선 형태로 정리했다. +- 로그인 모달에 `로그인 유지` 체크박스를 추가했다. 기본값은 OFF이며, OFF 상태에서는 인증 토큰을 `sessionStorage`에 저장해 브라우저 세션이 끝나면 사라지고, ON 상태에서만 `localStorage`에 저장한다. +- 현재 로그아웃은 프론트 저장 토큰을 지우는 수준이다. 개인 기록 서비스 성격을 고려하면 다음 단계에서 서버 세션 폐기 API와 미사용 자동 로그아웃 옵션을 추가하는 편이 좋다. ## 갱신 규칙 diff --git a/TODO.md b/TODO.md index f2951e5..6dc1674 100644 --- a/TODO.md +++ b/TODO.md @@ -94,6 +94,10 @@ - [ ] 이메일 인증 플로우를 설계하고 구현한다. - [ ] 비밀번호 찾기 / 재설정 토큰 흐름을 추가한다. - [ ] 로그인 및 인증 관련 rate limit / 잠금 정책을 추가한다. +- [x] 로그인 유지 여부를 사용자가 선택할 수 있게 한다. +- [ ] 일정 시간 미사용 시 자동 로그아웃 옵션을 추가한다. +- [ ] 설정 화면에서 현재 기기 로그인 상태와 저장 방식을 안내한다. +- [ ] 서버 세션을 명시적으로 폐기하는 로그아웃 API를 추가한다. - [ ] 메일 발송 인프라와 발신 도메인 정책을 확정한다. - [ ] Resend 무료 플랜 대체 수단으로 SES 또는 일반 SMTP 연동 방식을 확정한다. - [ ] 관리자 페이지에서 계정 비활성화 / 강제 로그아웃 / 삭제 기능을 추가한다. diff --git a/src/App.vue b/src/App.vue index 5a04b14..77a7fff 100644 --- a/src/App.vue +++ b/src/App.vue @@ -59,6 +59,7 @@ const authForm = reactive({ nickname: '', email: '', password: '', + rememberSession: false, }) const goalForm = reactive({ title: '', @@ -1062,6 +1063,7 @@ function resetAuthForm() { authForm.nickname = '' authForm.email = '' authForm.password = '' + authForm.rememberSession = false } function resetGoalForm() { @@ -1099,13 +1101,14 @@ function updateAuthField({ field, value }) { authForm[field] = value } -async function applyAuthSuccess(data) { +async function applyAuthSuccess(data, persist = false) { authToken.value = data.token currentUser.value = data.user setSyncFeedback('cloud', '클라우드 동기화 연결됨') persistAuthState({ token: data.token, user: data.user, + persist, }) await loadGoals() await hydratePlannerRecordsFromApi() @@ -1131,7 +1134,7 @@ async function submitAuthForm() { password: authForm.password, }) - await applyAuthSuccess(result) + await applyAuthSuccess(result, authMode.value === 'login' && authForm.rememberSession) } catch (error) { authMessage.value = toUserFacingApiError(error, '인증 처리 중 문제가 발생했습니다.') } finally { @@ -1156,6 +1159,7 @@ async function restoreAuthSession() { persistAuthState({ token: savedAuth.token, user: result.user, + persist: savedAuth.persist, }) await loadGoals() await hydratePlannerRecordsFromApi() @@ -2015,12 +2019,13 @@ onBeforeUnmount(() => { >
-

회원 전용 플래너

+

MEMBERS ONLY

- 10 Minute Planner + 오늘을 흘려보내지 않는
+ 10분의 기록

- 로그인하면 날짜별 플래너, 통계, 목표를 내 계정 기준으로 이어서 사용할 수 있습니다. + 할 일, 집중 시간, 짧은 코멘트를 한 장의 다이어리로 남기고 내일의 첫 작업까지 이어가세요.

diff --git a/src/components/AuthDialog.vue b/src/components/AuthDialog.vue index 9145469..73e1dde 100644 --- a/src/components/AuthDialog.vue +++ b/src/components/AuthDialog.vue @@ -32,7 +32,7 @@ const emit = defineEmits([ function updateField(field, event) { emit('update:field', { field, - value: event.target.value, + value: event.target.type === 'checkbox' ? event.target.checked : event.target.value, }) } @@ -98,6 +98,24 @@ function updateField(field, event) { />
+ +