하관우 하관우 03-27
2025-03-27 하관우 카테고리 회원관리 완료
@3de6a824c1e34adbdf4804201d3ea9c79030fec0
client/resources/api/index.js
--- client/resources/api/index.js
+++ client/resources/api/index.js
@@ -65,6 +65,7 @@
                         store.commit("setUserNm", mbr.userNm); // 사용자 이름 저장
                         store.commit('setRoles', mbr.roles); // 사용자 역할 저장
 
+                        originalReq._retry = true; // 재시도 플래그 설정
                         return client(originalReq); // 원래 요청 재시도
                     } else {
                         // 200이 아닌 경우
client/resources/api/user.js
--- client/resources/api/user.js
+++ client/resources/api/user.js
@@ -20,6 +20,11 @@
     return apiClient.get(`/user/${userId}/users.json`);
 }
 
+// 회원가입
+export const join = mber => {
+    return apiClient.post(`/user/join.json`, mber);
+}
+
 // 유저 정보 변경
 export const updateUsers = (userId, updateUserDTO) => {
     return apiClient.put(`/user/${userId}/users.json`, updateUserDTO);
@@ -28,4 +33,9 @@
 // 유저 비밀번호 변경
 export const updatePassword = (userId, passwordDTO) => {
     return apiClient.put(`/user/${userId}/updatePassword.json`, passwordDTO);
-}
(파일 끝에 줄바꿈 문자 없음)
+}
+
+// 유저저 목록 조회 (검색조건 있음)
+export const findAllUsers = searchReqDTO => {
+    return apiClient.get(`/user/users.json`, { params: searchReqDTO });
+  }
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/user/CategoryManagement.vue
--- client/views/pages/user/CategoryManagement.vue
+++ client/views/pages/user/CategoryManagement.vue
@@ -41,9 +41,9 @@
             </div>
             <div class="righi-con wfull">
                 <div class="btn-group-small flex-end mb-20">
-                    <div v-if="copySelectedCategory.useAt === 'N' && isNewInsert && selectedCategory.ctgryId != null"
+                    <div v-if="copySelectedCategory.useAt === 'N' && isNewInsert && ctgryId != null"
                         class="button green-line" @click="updateByUseAtCategory">복구</div>
-                    <div v-if="copySelectedCategory.useAt === 'Y' && isNewInsert && selectedCategory.ctgryId != null"
+                    <div v-if="copySelectedCategory.useAt === 'Y' && isNewInsert && ctgryId != null"
                         class="button red-line" @click="updateByUseAtCategory">삭제</div>
                     <div class="button pink-line-bg flex align-center" @click="resetCategory"><img :src="check_pink"
                             alt="">
@@ -108,9 +108,9 @@
             check_blue: 'client/resources/images/checkbox_blue.png',
             searchicon: 'client/resources/images/icon/search.png',
             selectedCategories: [],
+            ctgryId: null,
             // 선택된 카테고리 정보를 저장할 객체
             selectedCategory: {
-                ctgryId: null,
                 ctgryNm: null,
                 useAt: null,
                 dc: null
@@ -128,6 +128,7 @@
                 searchText: null,
                 useAt: null
             },
+            //신규등록 체크크
             isNewInsert: true,
         };
     },
@@ -141,7 +142,6 @@
                 if (response.status === 200) {
                     this.selectedCategories = response.data.data.ctgry; // API 응답에서 카테고리 목록을 가져옴
                     console.log("가져온 카테고리들 ", this.searchCategories);
-                    this.selectedCategory = {}
                 }
             } catch (error) {
                 console.error("검색 중 오류 발생:", error);
@@ -150,13 +150,14 @@
         // 클릭한 카테고리의 정보를 오른쪽에 표시
         selectCategory(item) {
             this.isNewInsert = true;
+            this.ctgryId = item.ctgryId,
             this.selectedCategory = {
-                ctgryId: item.ctgryId,
                 ctgryNm: item.ctgryNm,
                 useAt: item.useAt,
                 dc: item.dc
             };
             this.copySelectedCategory = {
+                oldCtgryNm: item.ctgryNm,
                 ctgryId: item.ctgryId,
                 ctgryNm: item.ctgryNm,
                 useAt: item.useAt,
@@ -171,7 +172,7 @@
                 this.selectedCategory.useAt = 'N'
                 if (confirm("선택한 카테고리를 삭제 하시겠습니까?")) {
                     try {
-                        const response = await updateCategory(this.selectedCategory.ctgryId, this.selectedCategory);
+                        const response = await updateCategory(this.ctgryId, this.selectedCategory);
                         if (response.status === 200) {
                             alert("삭제되었습니다..");
                             this.searchCategories();
@@ -186,7 +187,7 @@
                 this.selectedCategory.useAt = 'Y'
                 if (confirm("선택한 카테고리를 복구 하시겠습니까?")) {
                     try {
-                        const response = await updateCategory(this.selectedCategory.ctgryId, this.selectedCategory);
+                        const response = await updateCategory(this.ctgryId, this.selectedCategory);
                         if (response.status === 200) {
                             alert("복구되었습니다..");
                             this.searchCategories();
@@ -199,11 +200,11 @@
                 }
             }
         },
-        // 신규 등록록
+        // 신규 등록
         resetCategory() {
             this.isNewInsert = false
+            this.ctgryId = null;
             this.selectedCategory = {
-                ctgryId: null,
                 ctgryNm: null,
                 useAt: null,
                 dc: null
@@ -218,10 +219,10 @@
         // 등록 및 수정
         async insertByUpdateCategory() {
             if (!this.isValidationCategory()) {
-                if (this.selectedCategory.ctgryId == null || this.selectedCategory.ctgryId == '') {
+                if (this.ctgryId == null || this.ctgryId == '') {
                     if (confirm("카테고리 등록을 하시겠습니까?")) {
                         try {
-                            const response = await saveCategory(this.selectedCategory.ctgryId, this.selectedCategory);
+                            const response = await saveCategory(this.selectedCategory);
                             if (response.status === 200) {
                                 alert("등록되었습니다..");
                                 this.searchCategories();
@@ -232,10 +233,12 @@
                         }
                     }
                 } else {
-                    if (this.copySelectedCategory.ctgryNm == this.selectCategory.ctgryNm) {
+                    this.updateCategory();
+                    if (this.copySelectedCategory.oldCtgryNm == this.copySelectedCategory.ctgryNm) {
+                        this.copySelectedCategory.oldCtgryNm = null;
                         if (confirm("카테고리 수정을 하시겠습니까?")) {
                             try {
-                                const response = await updateCategory(this.selectedCategory.ctgryId, this.selectedCategory);
+                                const response = await updateCategory(this.ctgryId, this.copySelectedCategory);
                                 if (response.status === 200) {
                                     alert("수정되었습니다..");
                                     this.searchCategories();
@@ -248,8 +251,7 @@
                     } else {
                         if (confirm("카테고리 수정을 하시겠습니까?")) {
                             try {
-                                this.selectCategory.oldCtgryNm = this.copySelectedCategory.ctgryNm;
-                                const response = await updateCategory(this.selectedCategory.ctgryId, this.selectedCategory);
+                                const response = await updateCategory(this.ctgryId, this.copySelectedCategory);
                                 if (response.status === 200) {
                                     alert("수정되었습니다..");
                                     this.searchCategories();
@@ -269,9 +271,10 @@
         toggleUseAt() {
             this.selectedCategory.useAt = this.selectedCategory.useAt === 'Y' ? 'N' : 'Y';
         },
+        //취소소
         cancelCategory() {
+            this.ctgryId = this.copySelectedCategory.ctgryId,
             this.selectedCategory = {
-                ctgryId: this.copySelectedCategory.ctgryId,
                 ctgryNm: this.copySelectedCategory.ctgryNm,
                 useAt: this.copySelectedCategory.useAt,
                 dc: this.copySelectedCategory.dc,
@@ -279,8 +282,14 @@
         },
         //카테고리 벨류데이션 체크
         isValidationCategory() {
-            return this.selectedCategory.ctgryNm == null || this.selectedCategory.ctgryNm == '' || this.selectedCategory.useAt == null || this.selectedCategory.useAt == '';
+            return this.selectedCategory.ctgryNm == null || this.selectedCategory.ctgryNm == '';
         },
+        //수정 함수
+        updateCategory(){
+            this.copySelectedCategory.ctgryNm = this.selectedCategory.ctgryNm;
+            this.copySelectedCategory.useAt = this.selectedCategory.useAt;
+            this.copySelectedCategory.dc =  this.selectedCategory.dc;
+        }
 
     },
     mounted() {
client/views/pages/user/MemberManagement.vue
--- client/views/pages/user/MemberManagement.vue
+++ client/views/pages/user/MemberManagement.vue
@@ -17,17 +17,19 @@
                 <div class="search-wrap mb-20">
                     <div class="search-area">
                         <div class="select-box">
-                            <select name="" id="">
-                                <option value="all" selected>전체</option>
+                            <select v-model="searchReqDTO.searchType">
+                                <option value="" selected>전체</option>
                                 <option value="id">아이디</option>
-                                <option value="name">이름</option>
+                                <option value="nm">이름</option>
                             </select>
                         </div>
-                        <div class="wfull" style="height: 100%;"> <input type="text" v-model="searchText"></div>
-                        <button class="search-btn"><img :src="searchicon" alt=""></button>
+                        <div class="wfull" style="height: 100%;">
+                            <input type="text" v-model="searchReqDTO.searchText" @keyup.enter="searchUsers">
+                        </div>
+                        <button class="search-btn" @click="searchUsers">
+                            <img :src="searchicon" alt="">
+                        </button>
                     </div>
-
-
                 </div>
                 <table class="mb-10">
                     <thead>
@@ -38,31 +40,27 @@
                         </tr>
                     </thead>
                     <tbody>
-                        <tr v-for="item in items" :key="index" :class="{ 'delete-member': item.delete }">
-                            <!-- Category 칼럼 -->
-                            <td>
-                                {{ item.id }}
-                            </td>
-                            <!-- Checkbox 칼럼 -->
-                            <td>
-                                {{ item.name }}
-                            </td>
-                            <td>
-                                {{ item.gownhan }}
-                            </td>
+                        <tr v-for="(item, index) in selectedUsers" :key="index"
+                            :class="{ 'delete-member': item.useAt === 'N' }" @click="selectUser(item)">
+                            <td>{{ item.loginId }}</td>
+                            <td>{{ item.userNm }}</td>
+                            <td>{{ item.authorList[0].authorNm }}</td>
                         </tr>
                     </tbody>
                 </table>
             </div>
             <div class="righi-con wfull">
                 <div class="btn-group-small flex-end mb-20">
-                    <div class="button green-line">복구</div>
-                    <div class="button red-line">회원탈퇴</div>
-                    <div class="button gray-line">비밀번호 초기화</div>
-                    <div class="button pink-line-bg flex align-center"><img :src="check_pink" alt="">
+                    <div v-if="copySelectedUser.useAt === 'N' && isNewInsert && userId != null"
+                        class="button green-line" @click="updateByUseAtUser">복구</div>
+                    <div v-if="copySelectedUser.useAt === 'Y' && isNewInsert && userId != null" class="button red-line"
+                        @click="updateByUseAtUser">회원탈퇴</div>
+                    <div class="button gray-line" @click="restPassword">비밀번호 초기화</div>
+                    <div class="button pink-line-bg flex align-center" @click="resetUser"><img :src="check_pink" alt="">
                         <p>신규등록</p>
                     </div>
-                    <div class="button blue-line-bg flex align-center"><img :src="check_blue" alt="">
+                    <div class="button blue-line-bg flex align-center" @click="insertByUpdateUser"><img
+                            :src="check_blue" alt="">
                         <p>등록</p>
                     </div>
                     <div class="button gray-bg">취소</div>
@@ -71,65 +69,62 @@
                     <dl>
                         <dd>
                             <label for="id" class="require">아이디</label>
-                            <input type="text" id="id" value="admin" readonly>
+                            <input type="text" id="id" v-model="loginId" :readonly="isNewInsert">
                         </dd>
                         <div class="hr"></div>
                         <dd>
-                            <label for="pw" class="require">비밀번호</label><input type="text" id="pw" value="">
-                            <div class="invalid-feedback border">
+                            <label for="pw" class="require">비밀번호</label>
+                            <input type="password" id="pw" v-model="password" :disabled="isNewInsert"
+                                :placeholder="isNewInsert ? '' : '비밀번호를 입력하세요'" @input="validatePassword">
+                            <div class="invalid-feedback border" v-if="!isPasswordValid && password !== null">
                                 <img :src="erroricon" alt="">
-                                <span>영문, 숫자, 특수문자를 최소 한 가지씩 조합하고 8자 이상 ~ 20자 이내로 입력해주세요.</span>
+                                <span>영문, 숫자, 특수문자를 최소 한 가지씩 조합하고 9자 이상 ~ 20자 이내로 입력해주세요.</span>
                             </div>
-
                         </dd>
                         <div class="hr"></div>
                         <dd>
                             <label for="pwcheck" class="require">비밀번호 확인</label>
-                            <input type="text" id="pwcheck" value="">
-                            <div class="invalid-feedback border">
+                            <input type="password" id="pwcheck" v-model="passwordCheck" :disabled="isNewInsert"
+                                :placeholder="isNewInsert ? '' : '비밀번호를 재입력하세요'">
+                            <div class="invalid-feedback border" v-if="passwordCheck !== null && !passwordsMatch">
                                 <img :src="erroricon" alt="">
                                 <span>비밀번호가 일치하지 않습니다.</span>
                             </div>
-
                         </dd>
                         <div class="hr"></div>
                         <dd>
                             <label for="name" class="require">이름</label>
-                            <input type="text" id="name" value="관리자" readonly>
-
+                            <input type="text" id="name" v-model="selectedUser.userNm">
                         </dd>
                         <div class="hr"></div>
                         <dd>
                             <label for="gwonhan" class="require">권한</label>
                             <div class="select-box">
-                                <select>
-                                    <option selected>관리자</option>
-                                    <option></option>
-                                    <option></option>
+                                <select v-model="selectedUser.authorList[0].authorCode">
+                                    <option value="ROLE_ADMIN" selected>관리자</option>
+                                    <option value="ROLE_USER">사용자</option>
                                 </select>
                             </div>
-
                         </dd>
                         <div class="hr"></div>
                         <dd>
                             <label for="use" class="require">사용여부</label>
                             <div class="switch">
-                                <input type="checkbox" id="switch" />
+                                <input type="checkbox" id="switch" :checked="selectedUser.useAt === 'Y'"
+                                    @change="toggleUseAt" />
                                 <label for="switch">Toggle</label>
-
                             </div>
-
                         </dd>
                     </dl>
                 </form>
             </div>
-
         </div>
-
     </div>
 </template>
+
 <script>
 import { DoubleLeftOutlined, LeftOutlined, RightOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
+import { findAllUsers, updateUsers, updatePassword, join } from "../../../resources/api/user";
 
 export default {
     components: {
@@ -141,32 +136,271 @@
 
     data() {
         return {
-            items: [
-                { id: 'user1', name: '카테고리 1', gownhan: '관리자', delete: false },
-                { id: 'user2', name: '카테고리 2', gownhan: '사용자', delete: false },
-                { id: 'user3', name: '카테고리 3', gownhan: '사용자', delete: true },
-            ],
-            isModalOpen: false,
-            // Define the image sources
             homeicon: 'client/resources/images/icon/home.png',
             erroricon: 'client/resources/images/icon/error.png',
             righticon: 'client/resources/images/icon/right.png',
             check_pink: 'client/resources/images/checkbox_pink.png',
             check_blue: 'client/resources/images/checkbox_blue.png',
             searchicon: 'client/resources/images/icon/search.png',
-            fileNames: [],
-            selectedCategories: [],
+            selectedUsers: [],
+            //사용자 로그인 아이다
+            loginId: null,
+            //사용자 아이디
+            userId: null,
+            // 비밀번호 추가
+            password: null,
+            // 비밀번호 확인 추가
+            passwordCheck: null,
+            // 비밀번호 유효성 체크를 위한 변수
+            isPasswordValid: false,
+            // 비밀번호 초기화 
+            passwordResetAt: {
+                oldPassword: "qwer1234!",
+                newPassword: "qwer1234!",
+                resetAt: true
+            },
+            // 선택된 사용자 수정
+            selectedUser: {
+                userNm: null,
+                useAt: null,
+                authorUpdateCheck: false,
+                authorList: [
+                    { authorCode: "ROLE_ADMIN" }
+                ]
+            },
+            // 선택된 사용자 복사 
+            copySelectedUser: {
+                userNm: null,
+                useAt: null,
+                authorList: [
+                    { authorCode: "ROLE_ADMIN" }
+                ]
+            },
+            //회원가입
+            joinDTO: {
+                loginId: null,
+                userNm: null,
+                password: null,
+                authorList: []
+            },
+            // 사용자 검색 객체
+            searchReqDTO: {
+                searchType: "",
+                searchText: null,
+                userSttus: null,
+                useAt: null
+            },
+            // 신규등록 체크
+            isNewInsert: true,
         };
     },
+    methods: {
+        //등록 함수
+        updateUser() {
+            this.joinDTO.loginId = this.loginId;
+            this.joinDTO.userNm = this.selectedUser.userNm;
+            this.joinDTO.password = this.password;
+            this.joinDTO.authorList = [this.selectedUser.authorList[0]];
+        },
+        // 등록 및 수정
+        async insertByUpdateUser() {
+            if (!this.isValidationUser()) {
+                if (this.userId == null || this.userId == '') {
+                    this.updateUser();
+                    if (confirm("회원가입을 하시겠습니까?")) {
+                        try {
+                            const response = await join(this.joinDTO);
+                            if (response.status === 200) {
+                                alert("회원가입되었습니다..");
+                                this.searchUsers();
+
+                            }
+                        } catch (error) {
+                            alert("회원가입이 실패하였습니다.");
+                            this.searchUsers();
+                        }
+                    }
+                }
+            } else {
+                // 권한이 변경되었는지 확인
+                console.log("복사", this.copySelectedUser.authorList[0].authorCode);
+                console.log("본체", this.selectedUser.authorList[0].authorCode);
+                const isAuthorChanged = this.copySelectedUser.authorList[0].authorCode !== this.selectedUser.authorList[0].authorCode;
+                console.log("권한이 같은지 다른지 ", isAuthorChanged);
+                if (isAuthorChanged) {
+                    this.selectedUser.authorUpdateCheck = true; // 권한 변경 체크
+                    if (confirm("회원 수정을 하시겠습니까?")) {
+                        try {
+                            const response = await updateUsers(this.userId, this.selectedUser);
+                            if (response.status === 200) {
+                                alert("수정되었습니다..");
+                                this.searchUsers();
+                            }
+                        } catch (error) {
+                            alert("수정이 실패하였습니다.");
+                            this.searchUsers();
+                        }
+                    }
+                } else {
+                    // 권한이 변경되지 않았다면 기존 수정 로직 실행
+                    if (confirm("회원 수정을 하시겠습니까?")) {
+                        try {
+                            const response = await updateUsers(this.userId, this.selectedUser);
+                            if (response.status === 200) {
+                                alert("수정되었습니다..");
+                                this.searchUsers();
+                            }
+                        } catch (error) {
+                            alert("수정이 실패하였습니다.");
+                            this.searchUsers();
+                        }
+                    }
+                }
+            }
+        },
+        //사용자 벨류데이션 체크
+        isValidationUser() {
+            return this.selectedUser.userNm == null || this.selectedUser.userNm == '' || this.loginId == null || this.loginId == '' || this.password == null || this.password == '';
+        },
+        validatePassword() {
+            // 빈 문자열이나 null 체크
+            if (!this.password) {
+                this.isPasswordValid = false; // 빈 문자열일 경우 유효성 false
+                return;
+            }
+
+            // 정규식: 영문, 숫자, 특수문자를 최소 한 가지씩 조합하고 8자 이상 20자 이내
+            const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{9,20}$/;
+            this.isPasswordValid = passwordRegex.test(this.password);
+        },
+        // 사용자 전체조회
+        async searchUsers() {
+            try {
+                const response = await findAllUsers(this.searchReqDTO);
+                if (response.status === 200) {
+                    this.selectedUsers = response.data.data.users;
+                    console.log("가져온 사용자들 ", response);
+                }
+            } catch (error) {
+                console.error("검색 중 오류 발생:", error);
+            }
+        },
+        // 선택한 사용자
+        selectUser(item) {
+            this.isNewInsert = true;
+
+            this.loginId = item.loginId;
+            this.userId = item.userId;
+            this.password = null;
+            this.passwordCheck = null;
+
+            this.selectedUser = {
+                userNm: item.userNm,
+                useAt: item.useAt,
+                authorList: [item.authorList[0]]
+            };
+
+            // 깊은 복사를 통해 copySelectedUser를 설정
+            this.copySelectedUser = JSON.parse(JSON.stringify(this.selectedUser));
+
+            console.log("선택 된 카테고리", this.selectedUser.authorList[0]);
+        },
+        // 스위치 사용여부
+        toggleUseAt() {
+            if (!this.isNewInsert) {
+                alert("회원가입 시 사용여부 변경이 불가능합니다.");
+                return; // 변경을 막기 위해 함수 종료
+            }
+
+            this.selectedUser.useAt = this.selectedUser.useAt === 'Y' ? 'N' : 'Y';
+        },
+        // 삭제 또는 복구 로직
+        async updateByUseAtUser() {
+            if (this.selectedUser.useAt === 'Y') {
+                this.selectedUser.useAt = 'N'
+                if (confirm("선택한 사용자를 탈퇴 하시겠습니까?")) {
+                    try {
+                        const response = await updateUsers(this.userId, this.selectedUser);
+                        if (response.status === 200) {
+                            alert("탈퇴되었습니다..");
+                            this.copySelectedUser.useAt = 'N';
+                            this.searchUsers();
+                        }
+                    } catch (error) {
+                        alert("탈퇴가 실패하였습니다.");
+                        this.searchUsers();
+
+                    }
+                }
+            } else {
+                this.selectedUser.useAt = 'Y'
+                if (confirm("선택한 사용자를 복구 하시겠습니까?")) {
+                    try {
+                        const response = await updateUsers(this.userId, this.selectedUser);
+                        if (response.status === 200) {
+                            alert("복구되었습니다..");
+                            this.copySelectedUser.useAt = 'Y';
+                            this.searchUsers();
+                        }
+                    } catch (error) {
+                        alert("복구가 실패하였습니다.");
+                        this.searchUsers();
+
+                    }
+                }
+            }
+        },
+        // 비밀번호 초기화 
+        async restPassword() {
+            if (confirm("비밀번호 초기화를 하시겠습니까?")) {
+                if (this.userId == null || this.userId == '') {
+                    alert("신규등록은 초기화가 불가능합니다.")
+                } else {
+                    try {
+                        const response = await updatePassword(this.userId, this.passwordResetAt);
+                        if (response.status === 200) {
+                            alert("초기화화되었습니다..");
+                            this.searchUsers();
+                        }
+                    } catch (error) {
+                        alert("초기화가 실패하였습니다.");
+                        this.searchUsers();
+
+                    }
+                }
+            }
+        },
+        // 사용자 초기화
+        resetUser() {
+            this.isNewInsert = false;
+
+            this.loginId = null;
+            this.userId = null;
+
+            this.selectedUser = {
+                userNm: null,
+                useAt: "Y",
+                authorList: [
+                    { authorCode: "ROLE_ADMIN" }
+                ]
+            };
+
+            this.copySelectedUser = {
+                userNm: null,
+                useAt: null,
+                authorList: [
+                    { authorCode: "ROLE_ADMIN" }
+                ]
+            };
+        },
+    },
     computed: {
-        filteredItems() {
-            // This could be modified to support filtering based on searchQuery
-            return this.items.filter(item =>
-                item.id.includes(this.searchQuery)
-            );
+        passwordsMatch() {
+            return this.password === this.passwordCheck;
         }
     },
-    methods: {
+    mounted() {
+        this.searchUsers(); // 컴포넌트가 마운트될 때 사용자 목록을 조회
     }
 };
 </script>
client/views/pages/user/MyInfo.vue
--- client/views/pages/user/MyInfo.vue
+++ client/views/pages/user/MyInfo.vue
@@ -41,7 +41,7 @@
                         @input="validatePassword">
                     <div class="invalid-feedback border" v-if="!isPasswordValid && userPassword.newPassword !== null">
                         <img :src="erroricon" alt="">
-                        <span>영문, 숫자, 특수문자를 최소 한 가지씩 조합하고 8자 이상 ~ 20자 이내로 입력해주세요.</span>
+                        <span>영문, 숫자, 특수문자를 최소 한 가지씩 조합하고 9자 이상 ~ 20자 이내로 입력해주세요.</span>
                     </div>
                 </dd>
                 <div class="hr"></div>
@@ -103,8 +103,8 @@
                 return;
             }
 
-            // 정규식: 영문, 숫자, 특수문자를 최소 한 가지씩 조합하고 8자 이상 20자 이내
-            const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,20}$/;
+            // 정규식: 영문, 숫자, 특수문자를 최소 한 가지씩 조합하고 9자 이상 20자 이내
+            const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{9,20}$/;
             this.isPasswordValid = passwordRegex.test(this.userPassword.newPassword);
         },
         async fnDeleteUser() {
Add a comment
List