feat: React 18 프론트엔드 추가 및 프로젝트 구조 정리
- wtm-frontend → wtm-frontend-vue 이름 변경 - wtm-frontend-react 추가 (React 18 + PrimeReact + Zustand) - 동일한 모듈 구조 및 API 연동 (Vue 버전과 기능 동일) - Vue:5173 / React:5174 포트 분리 - 개발자 가이드에 React 프론트엔드 안내 추가 - .gitignore: Claude/OMC, 문서 생성 스크립트, package-lock 제외 - 불필요 파일 git 추적 제거 (.omc, generate_*.py, regenerate_*.py) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
이 Commit은 다음에 포함되어 있습니다:
@@ -0,0 +1,133 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import Password from 'primevue/password';
|
||||
import Button from 'primevue/button';
|
||||
import Card from 'primevue/card';
|
||||
import Message from 'primevue/message';
|
||||
import { authApi } from '../auth.service';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const currentPassword = ref('');
|
||||
const newPassword = ref('');
|
||||
const confirmPassword = ref('');
|
||||
const error = ref('');
|
||||
const success = ref('');
|
||||
const loading = ref(false);
|
||||
|
||||
async function onSubmit() {
|
||||
error.value = '';
|
||||
success.value = '';
|
||||
|
||||
if (newPassword.value !== confirmPassword.value) {
|
||||
error.value = '새 비밀번호가 일치하지 않습니다.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword.value.length < 8) {
|
||||
error.value = '비밀번호는 최소 8자 이상이어야 합니다.';
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
await authApi.changePassword({
|
||||
currentPassword: currentPassword.value,
|
||||
newPassword: newPassword.value,
|
||||
});
|
||||
success.value = '비밀번호가 변경되었습니다.';
|
||||
currentPassword.value = '';
|
||||
newPassword.value = '';
|
||||
confirmPassword.value = '';
|
||||
setTimeout(() => router.push('/dashboard'), 1500);
|
||||
} catch (e: any) {
|
||||
error.value = e?.response?.data?.detail ?? '비밀번호 변경에 실패했습니다.';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="change-password-page">
|
||||
<Card class="change-password-page__card">
|
||||
<template #title>비밀번호 변경</template>
|
||||
<template #content>
|
||||
<Message v-if="error" severity="error" :closable="false" style="width: 100%; margin-bottom: 1rem;">
|
||||
{{ error }}
|
||||
</Message>
|
||||
<Message v-if="success" severity="success" :closable="false" style="width: 100%; margin-bottom: 1rem;">
|
||||
{{ success }}
|
||||
</Message>
|
||||
|
||||
<form class="change-password-page__form" @submit.prevent="onSubmit">
|
||||
<div class="form-field">
|
||||
<label class="form-field__label">현재 비밀번호</label>
|
||||
<Password
|
||||
v-model="currentPassword"
|
||||
placeholder="현재 비밀번호 입력"
|
||||
:feedback="false"
|
||||
toggleMask
|
||||
fluid
|
||||
:inputStyle="{ width: '100%' }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label class="form-field__label">새 비밀번호</label>
|
||||
<Password
|
||||
v-model="newPassword"
|
||||
placeholder="새 비밀번호 입력"
|
||||
toggleMask
|
||||
fluid
|
||||
:inputStyle="{ width: '100%' }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label class="form-field__label">새 비밀번호 확인</label>
|
||||
<Password
|
||||
v-model="confirmPassword"
|
||||
placeholder="새 비밀번호 다시 입력"
|
||||
:feedback="false"
|
||||
toggleMask
|
||||
fluid
|
||||
:inputStyle="{ width: '100%' }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
label="비밀번호 변경"
|
||||
icon="pi pi-check"
|
||||
:loading="loading"
|
||||
fluid
|
||||
style="margin-top: 0.5rem;"
|
||||
/>
|
||||
</form>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.change-password-page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 80vh;
|
||||
padding: 1rem;
|
||||
|
||||
&__card {
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
&__form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
새 Issue에서 참조
사용자 차단