회원탈퇴 팝업 UI 개선 및 관련 JS 이벤트 처리 추가

This commit is contained in:
Rinjae
2026-03-05 15:24:37 +09:00
parent d22a1ad535
commit c1f584cbf1
6 changed files with 250 additions and 66 deletions
+143
View File
@@ -329,6 +329,149 @@ src/main/java/com/eactive/apim/portal/
- `elink-portal-common`: 공통 유틸리티 (../elink-portal-common)
- `kjb-safedb`: SafeDB 암호화 라이브러리 (../kjb-safedb)
## SASS / CSS 스타일 개발
### 개요
이 프로젝트는 **Dart Sass**를 사용하여 CSS를 관리합니다.
`src/main/resources/static/css/main.css`는 SASS에서 컴파일된 결과물이므로 **직접 수정하면 안 됩니다.**
스타일 변경은 반드시 `src/main/resources/static/sass/` 하위 파일을 수정하세요.
### 설치
```bash
# Node.js가 설치된 상태에서
npm install
```
### 빌드 명령어
```bash
# 1회 컴파일 (개발용, 들여쓰기 유지)
npm run sass:build
# 1회 컴파일 (배포용, minified)
npm run sass:build:minified
# 파일 변경 감지 후 자동 컴파일 (개발 중 권장)
npm run sass:watch
# 빌드 + minified 동시 생성
npm run build
# 단축 스크립트 (npm 없이 sass 직접 실행)
./sass-build.sh
```
### SASS 파일 구조
```
src/main/resources/static/sass/
├── main.scss # 진입점 - 모든 파일을 @use로 조합
├── abstracts/ # CSS 출력 없는 헬퍼 (변수, 믹스인 등)
│ ├── _variables.scss # 색상, 타이포그래피, 그림자 변수
│ ├── _mixins.scss # 재사용 믹스인
│ └── _color-functions.scss # Dart Sass 3.0 color 유틸리티
├── base/ # 전역 기본 스타일
│ ├── _reset.scss
│ ├── _typography.scss
│ ├── _animations.scss
│ ├── _kjb-font.scss # 광주은행 전용 폰트
│ └── _utilities.scss # 유틸리티 클래스
├── layout/ # 페이지 레이아웃 구조
│ ├── _header.scss
│ ├── _footer.scss
│ ├── _grid.scss
│ └── _container.scss
├── components/ # 재사용 UI 컴포넌트
│ ├── _buttons.scss
│ ├── _forms.scss
│ ├── _modals.scss # customPopup 모달 스타일
│ ├── _tables.scss
│ ├── _badges.scss
│ ├── _cards.scss
│ ├── _navigation.scss
│ ├── _pagination.scss
│ └── ...
├── pages/ # 페이지별 스타일
│ ├── _mypage.scss
│ ├── _login.scss
│ ├── _apikey-list.scss
│ └── ...
├── themes/
│ └── _dark.scss # 다크 테마 (예약)
└── vendors/
└── _overrides.scss # 외부 라이브러리 스타일 덮어쓰기
```
### 새 스타일 추가하는 방법
#### 1. 기존 컴포넌트/페이지 파일에 추가
해당 `_*.scss` 파일을 직접 수정합니다.
```scss
// components/_buttons.scss 예시
.btn-withdrawal {
color: $text-gray;
border: 1px solid $border-gray;
&:hover {
color: $accent-orange;
}
}
```
#### 2. 새 페이지 스타일 파일 추가
```bash
# 1. 파일 생성 (언더스코어 접두사 필수)
touch src/main/resources/static/sass/pages/_new-page.scss
# 2. main.scss 에 @use 추가
```
```scss
// main.scss 에 추가
@use 'pages/new-page' as *;
```
#### 3. 변수 활용 예시
```scss
// abstracts/_variables.scss 에 정의된 변수 사용
.my-component {
color: $primary-blue; // #0049b4 (광주은행 블루)
background: $gray-bg; // #F8FAFC
border: 1px solid $border-gray; // #E2E8F0
box-shadow: $shadow-md;
font-family: $font-family-primary;
}
```
### 컴파일 결과 확인
```bash
# 컴파일 후 출력 파일 확인
ls -lh src/main/resources/static/css/main.css
ls -lh src/main/resources/static/css/main.min.css # minified
```
### 주의사항
- `src/main/resources/static/css/main.css` **직접 수정 금지** — SASS 재컴파일 시 덮어씌워짐
- `style2.css` 는 레거시 파일(구 디자인 시스템)이며 `main.css`**별도로 관리됨**
- `@import` 대신 **`@use`** 사용 (Dart Sass 3.0 호환)
- 파일명 앞에 언더스코어(`_`) 필수 — `_filename.scss` 형식이어야 단독 컴파일되지 않음
---
## AI 코딩 어시스턴트 지침 파일
이 프로젝트는 여러 AI 코딩 어시스턴트를 지원합니다. 각 도구는 다음 파일을 참조합니다:
@@ -576,6 +576,51 @@ const customPopups = {
$(document).off('keydown.changeRole');
},
showWithdrawal: function () {
$('#withdrawalPopup').show();
setTimeout(function() {
$('#withdrawalModalBackdrop').addClass('show');
$('#withdrawalModal').addClass('show');
}, 10);
$('body').css('overflow', 'hidden');
$('#withdrawalPopupConfirmButton').off('click').on('click', function () {
customPopups.hideWithdrawal();
$('#withdrawalForm').submit();
});
$('#withdrawalPopupCancelButton').off('click').on('click', function () {
customPopups.hideWithdrawal();
});
$('#withdrawalPopupCloseButton').off('click').on('click', function () {
customPopups.hideWithdrawal();
});
$(document).off('keydown.withdrawal').on('keydown.withdrawal', function (e) {
if (e.key === 'Escape') {
customPopups.hideWithdrawal();
}
});
},
hideWithdrawal: function () {
$('#withdrawalModalBackdrop').removeClass('show');
$('#withdrawalModal').removeClass('show');
setTimeout(function() {
$('#withdrawalPopup').hide();
}, 300);
$('body').css('overflow', '');
$('#withdrawalPopupConfirmButton').off('click');
$('#withdrawalPopupCancelButton').off('click');
$('#withdrawalPopupCloseButton').off('click');
$(document).off('keydown.withdrawal');
},
init: function () {
// emailValidationPopup 팝업의 닫기 버튼에 이벤트 리스너 추가
$('#emailValidationPopupCloseButton').on('click', function () {
@@ -225,6 +225,7 @@
</div>
</div>
</div>
</section>
<th:block layout:fragment="contentScript">
<script th:if="${error}" th:inline="javascript">
@@ -686,8 +687,20 @@
updateHiddenFields();
initializePhoneNumbers();
// 회원탈퇴 버튼 이벤트
const withdrawalBtn = document.querySelector('.withdrawal-link');
if (withdrawalBtn) {
withdrawalBtn.addEventListener('click', (e) => {
e.preventDefault();
customPopups.showWithdrawal();
});
}
});
</script>
</th:block>
<section layout:fragment="pagePopups">
<th:block th:replace="~{fragment/popup/withdrawalPopup :: withdrawalPopup}"></th:block>
</section>
</body>
</html>
@@ -40,6 +40,7 @@
</div>
</div>
</div>
</section>
<th:block layout:fragment="contentScript">
<script th:if="${error}" th:inline="javascript">
@@ -151,11 +152,14 @@
if (withdrawalBtn) {
withdrawalBtn.addEventListener('click', (e) => {
e.preventDefault();
document.getElementById('withdrawalPopup').style.display = 'block';
customPopups.showWithdrawal();
});
}
});
</script>
</th:block>
<section layout:fragment="pagePopups">
<th:block th:replace="~{fragment/popup/withdrawalPopup :: withdrawalPopup}"></th:block>
</section>
</body>
</html>
@@ -56,6 +56,7 @@
</div>
</div>
</div>
</section>
<th:block layout:fragment="contentScript">
<script th:if="${error}" th:inline="javascript">
@@ -184,11 +185,14 @@
if (withdrawalBtn) {
withdrawalBtn.addEventListener('click', (e) => {
e.preventDefault();
document.getElementById('withdrawalPopup').style.display = 'block';
customPopups.showWithdrawal();
});
}
});
</script>
</th:block>
<section layout:fragment="pagePopups">
<th:block th:replace="~{fragment/popup/withdrawalPopup :: withdrawalPopup}"></th:block>
</section>
</body>
</html>
@@ -1,65 +1,40 @@
<section class="content pop" th:fragment="withdrawalPopup">
<!-- 회원탈퇴 팝업 -->
<div class="popup_total" id="withdrawalPopup" style="display:none; z-index: 1000;">
<div class="pop_dim">
<div class="inner">
<div class="popup-content">
<div class="inner_txt">
<div class="title">
<h2>API Portal 회원탈퇴</h2>
</div>
</div>
<form id="withdrawalForm" th:action="@{/withdraw}" method="POST">
<div class="pop_grid">
<div class="list-dot list">
<p>회원 탈퇴는 API Portal 사이트의 회원 탈퇴입니다.</p>
<p>사용중인 API 서비스의 중지는 제휴 담당자를 통해 문의 주시기 바랍니다.</p>
</div>
</div>
<div class="pop_btnbox">
<button class="popup_button_gray btn_cancel">취소</button>
<button class="popup_button_blue btn_withdraw">탈퇴신청</button>
</div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
</form>
<a class="btn_close">
<img th:src="@{/img/icon/icon_close2.png}" alt="닫기">
</a>
</div>
</div>
</div>
<!-- views/fragment/popup/withdrawalPopup.html -->
<div th:fragment="withdrawalPopup" id="withdrawalPopup" style="display: none;">
<!-- Modal Backdrop -->
<div class="modal-backdrop" id="withdrawalModalBackdrop"></div>
<!-- Modal Wrapper -->
<div class="modal" id="withdrawalModal">
<div class="modal-dialog">
<!-- Modal Header -->
<div class="modal-header">
<h3 class="modal-title">API Portal 회원탈퇴</h3>
<button type="button" class="modal-close" id="withdrawalPopupCloseButton">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<!-- Modal Body -->
<div class="modal-body">
<ul style="margin: 0; padding-left: 20px; color: #64748B; font-size: 14px; line-height: 1.8;">
<li>회원 탈퇴는 API Portal 사이트의 회원 탈퇴입니다.</li>
<li>사용중인 API 서비스의 중지는 제휴 담당자를 통해 문의 주시기 바랍니다.</li>
</ul>
<form id="withdrawalForm" th:action="@{/withdraw}" method="POST" style="display:none;">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
</form>
</div>
<!-- Modal Footer -->
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="withdrawalPopupCancelButton">취소</button>
<button type="button" class="btn btn-primary" id="withdrawalPopupConfirmButton">탈퇴신청</button>
</div>
</div>
<script>
$(document).ready(function() {
const withdrawalPopup = {
init: function() {
this.bindEvents();
},
bindEvents: function() {
$('.withdrawal a').on('click', function(e) {
e.preventDefault();
$('#withdrawalPopup').show();
});
// 팝업 닫기 - 닫기 버튼과 취소 버튼
$('#withdrawalPopup .btn_close, #withdrawalPopup .btn_cancel').on('click', function(e) {
e.preventDefault();
$('#withdrawalPopup').hide();
});
$('#withdrawalPopup .btn_withdraw').on('click', function(e) {
e.preventDefault();
withdrawalPopup.handleWithdrawal();
});
},
handleWithdrawal: function() {
$('#withdrawalForm').submit();
}
};
withdrawalPopup.init();
});
</script>
</section>
</div>
</div>