12 Commits

Author SHA1 Message Date
Rinjae 87763ba325 CSS 메인 파일 업데이트:
- "main.min.css"에서 디자인 스타일 및 애니메이션 수정 및 최적화.
2026-07-20 16:48:12 +09:00
Rinjae 5b7a4a108d design-merge 강제 병합 - 디자인 반영본 전체 통합
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 16:32:18 +09:00
hong 9b9bcb5be4 패딩 수정 2026-07-20 14:05:43 +09:00
hong 93f7c210bc 11 2026-07-14 15:52:10 +09:00
hong bb79b55f08 fix: 회원가입 , oauth 반응형 수정 2026-07-14 14:54:53 +09:00
hong 23bd0c1207 fix : 메인페이지 반응형 수정 2026-07-14 10:35:14 +09:00
hong 28a9ff1a7f feat: 마이페이지 퍼블리싱 2026-07-14 10:34:31 +09:00
hong 045f354e5c Q&A 리스트 퍼블리싱 2026-07-09 10:45:20 +09:00
hong 4f577c8c14 Faq , q&a 상세 , 피드백 , 마이페이지 이용자 관리 퍼블리싱 2026-07-09 09:47:16 +09:00
hong cc4c1a6b53 feat : 오픈 API & 공지사항 퍼블리싱 2026-07-06 17:21:58 +09:00
hong 551a2da717 feat: 회원가입 안내 및 oauth 2 페이지 퍼블리싱 2026-07-03 16:41:24 +09:00
hong 1c57d348e6 디자인 시안 적용 및 애니메이션 개선 2026-07-02 17:52:59 +09:00
19 changed files with 85 additions and 68 deletions
+2 -1
View File
@@ -254,7 +254,8 @@ CREATE TABLE DVPOWN.PT_MESSAGE_RECIPIENT
) )
; ;
create table DVPOWN.PT_TOKEN create table PT_TOKEN
(
( (
TOKEN VARCHAR2(255) not null TOKEN VARCHAR2(255) not null
primary key, primary key,
@@ -22,6 +22,10 @@ public class PortalApplication extends SpringBootServletInitializer {
private static final Logger portal_logger = LoggerFactory.getLogger(PortalApplication.class); private static final Logger portal_logger = LoggerFactory.getLogger(PortalApplication.class);
public PortalApplication() {
super();
}
public static void main(String[] args) { public static void main(String[] args) {
portal_logger.info("##### PortalApplication Start #####"); portal_logger.info("##### PortalApplication Start #####");
@@ -85,9 +85,9 @@ public class BaseDatasourceConfiguration {
persistenceUnit = "gateway"; persistenceUnit = "gateway";
} }
// 개발 환경용 // 개발 환경용 - local 프로파일에서는 스키마 검증(ddl-auto) 해제
if (env.matchesProfiles("local")) { if (env.matchesProfiles("local")) {
properties.put("hibernate.hbm2ddl.auto", "validate"); properties.put("hibernate.hbm2ddl.auto", "none");
} }
+2
View File
@@ -30,6 +30,8 @@ spring:
default-page-size: '10' default-page-size: '10'
jpa: jpa:
open-in-view: false open-in-view: false
hibernate:
ddl-auto: none
web: web:
resources: resources:
+30 -31
View File
@@ -5686,11 +5686,9 @@ select.form-control {
cursor: pointer; cursor: pointer;
} }
@media (max-width: 768px) { @media (max-width: 1699px) {
.session-float { .session-float {
top: 12px; display: none;
right: 72px;
padding: 4px 10px;
} }
} }
.data-table { .data-table {
@@ -16415,7 +16413,7 @@ input[type=checkbox]:checked + .custom-checkbox {
width: 100%; width: 100%;
} }
} }
.step2-wrap .s2-floating-cart { .s2-floating-cart {
position: fixed; position: fixed;
bottom: 80px; bottom: 80px;
right: 40px; right: 40px;
@@ -16434,13 +16432,13 @@ input[type=checkbox]:checked + .custom-checkbox {
transition: all 0.3s ease; transition: all 0.3s ease;
z-index: 1001; z-index: 1001;
} }
.step2-wrap .s2-floating-cart .s2-cart-label { .s2-floating-cart .s2-cart-label {
font-size: 14px; font-size: 14px;
font-weight: 700; font-weight: 700;
color: #ffffff; color: #ffffff;
white-space: nowrap; white-space: nowrap;
} }
.step2-wrap .s2-floating-cart .s2-cart-badge { .s2-floating-cart .s2-cart-badge {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
@@ -16452,47 +16450,48 @@ input[type=checkbox]:checked + .custom-checkbox {
flex-shrink: 0; flex-shrink: 0;
margin-right: -8px; margin-right: -8px;
} }
.step2-wrap .s2-floating-cart .s2-cart-badge .s2-cart-count { .s2-floating-cart .s2-cart-badge .s2-cart-count {
font-size: 24px; font-size: 24px;
font-weight: 700; font-weight: 700;
color: #212529; color: #212529;
line-height: 1; line-height: 1;
} }
.step2-wrap .s2-floating-cart .s2-cart-badge .s2-cart-unit { .s2-floating-cart .s2-cart-badge .s2-cart-unit {
font-size: 14px; font-size: 14px;
font-weight: 700; font-weight: 700;
color: #212529; color: #212529;
line-height: 1; line-height: 1;
margin-top: 2px; margin-top: 2px;
} }
.step2-wrap .s2-floating-cart:hover { .s2-floating-cart:hover {
transform: translateY(-4px); transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(59, 164, 237, 0.5); box-shadow: 0 12px 32px rgba(59, 164, 237, 0.5);
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.step2-wrap .s2-floating-cart { .s2-floating-cart {
bottom: 100px; bottom: 100px;
right: 24px; right: 24px;
width: 160px; width: 160px;
height: 56px; height: 56px;
padding: 8px 12px 8px 28px; padding: 8px 12px 8px 28px;
} }
.step2-wrap .s2-floating-cart .s2-cart-label { .s2-floating-cart .s2-cart-label {
font-size: 12px; font-size: 12px;
} }
.step2-wrap .s2-floating-cart .s2-cart-badge { .s2-floating-cart .s2-cart-badge {
width: 48px; width: 48px;
height: 48px; height: 48px;
margin-right: -6px; margin-right: -6px;
} }
.step2-wrap .s2-floating-cart .s2-cart-badge .s2-cart-count { .s2-floating-cart .s2-cart-badge .s2-cart-count {
font-size: 20px; font-size: 20px;
} }
.step2-wrap .s2-floating-cart .s2-cart-badge .s2-cart-unit { .s2-floating-cart .s2-cart-badge .s2-cart-unit {
font-size: 12px; font-size: 12px;
} }
} }
.step2-wrap .s2-modal {
.s2-modal {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
@@ -16503,7 +16502,7 @@ input[type=checkbox]:checked + .custom-checkbox {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.step2-wrap .s2-modal .s2-modal-backdrop { .s2-modal .s2-modal-backdrop {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@@ -16512,7 +16511,7 @@ input[type=checkbox]:checked + .custom-checkbox {
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(2px); backdrop-filter: blur(2px);
} }
.step2-wrap .s2-modal .s2-modal-dialog { .s2-modal .s2-modal-dialog {
position: relative; position: relative;
background: #ffffff; background: #ffffff;
border-radius: 20px; border-radius: 20px;
@@ -16525,40 +16524,40 @@ input[type=checkbox]:checked + .custom-checkbox {
overflow: hidden; overflow: hidden;
z-index: 1; z-index: 1;
} }
.step2-wrap .s2-modal .s2-modal-header { .s2-modal .s2-modal-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 20px 24px; padding: 20px 24px;
border-bottom: 1px solid #e8edf5; border-bottom: 1px solid #e8edf5;
} }
.step2-wrap .s2-modal .s2-modal-header .s2-modal-title { .s2-modal .s2-modal-header .s2-modal-title {
font-size: 18px; font-size: 18px;
font-weight: 700; font-weight: 700;
color: #0d0e11; color: #0d0e11;
margin: 0; margin: 0;
} }
.step2-wrap .s2-modal .s2-modal-header .s2-modal-close { .s2-modal .s2-modal-header .s2-modal-close {
background: none; background: none;
border: none; border: none;
font-size: 20px; font-size: 20px;
cursor: pointer; cursor: pointer;
color: #94a3b8; color: #94a3b8;
} }
.step2-wrap .s2-modal .s2-modal-header .s2-modal-close:hover { .s2-modal .s2-modal-header .s2-modal-close:hover {
color: #0d0e11; color: #0d0e11;
} }
.step2-wrap .s2-modal .s2-modal-body { .s2-modal .s2-modal-body {
padding: 24px; padding: 24px;
max-height: 300px; max-height: 300px;
overflow-y: auto; overflow-y: auto;
} }
.step2-wrap .s2-modal .s2-selected-list { .s2-modal .s2-selected-list {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 8px; gap: 8px;
} }
.step2-wrap .s2-modal .s2-api-pill { .s2-modal .s2-api-pill {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
@@ -16568,13 +16567,13 @@ input[type=checkbox]:checked + .custom-checkbox {
font-size: 14px; font-size: 14px;
color: #0d0e11; color: #0d0e11;
} }
.step2-wrap .s2-modal .s2-api-pill .s2-api-pill-name { .s2-modal .s2-api-pill .s2-api-pill-name {
max-width: 250px; max-width: 250px;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.step2-wrap .s2-modal .s2-api-pill .s2-api-pill-remove { .s2-modal .s2-api-pill .s2-api-pill-remove {
background: none; background: none;
border: none; border: none;
font-size: 14px; font-size: 14px;
@@ -16585,16 +16584,16 @@ input[type=checkbox]:checked + .custom-checkbox {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.step2-wrap .s2-modal .s2-api-pill .s2-api-pill-remove:hover { .s2-modal .s2-api-pill .s2-api-pill-remove:hover {
color: #f4253c; color: #f4253c;
} }
.step2-wrap .s2-modal .s2-modal-footer { .s2-modal .s2-modal-footer {
padding: 16px 24px; padding: 16px 24px;
border-top: 1px solid #e8edf5; border-top: 1px solid #e8edf5;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
} }
.step2-wrap .s2-modal .s2-btn-close-modal { .s2-modal .s2-btn-close-modal {
height: 40px; height: 40px;
padding: 0 20px; padding: 0 20px;
background: #f1f5f9; background: #f1f5f9;
@@ -16605,7 +16604,7 @@ input[type=checkbox]:checked + .custom-checkbox {
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
} }
.step2-wrap .s2-modal .s2-btn-close-modal:hover { .s2-modal .s2-btn-close-modal:hover {
background: #e2e8f0; background: #e2e8f0;
} }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -71,12 +71,12 @@
} }
} }
// 모바일: 헤더 우측 햄버거(메뉴 버튼) 바로 왼쪽에 나란히 배치. // 가로 여유가 있을 때만 노출.
// right = 헤더 padding(20px) + 햄버거 폭(~32px) + 간격(~20px) ≈ 72px // 헤더 .container(max-width:1280px, 중앙정렬)의 우측 여백에 위젯(폭 ~200px)이 들어갈 만큼
@media (max-width: $breakpoint-sm) { // 넓은 화면(약 1700px 이상)에서만 표시 → 그 외에는 헤더 우측 메뉴(사용자명·로그아웃·마이페이지)를 가리므로 숨김.
// (PC 전용: 모바일 ≤768px 도 당연히 숨김)
@media (max-width: 1699px) {
.session-float { .session-float {
top: 12px; display: none;
right: 72px;
padding: 4px 10px;
} }
} }
@@ -4027,7 +4027,8 @@ input[type="checkbox"]:checked+.custom-checkbox {
} }
// ── Floating Cart Button ── // ── Floating Cart Button ──
.s2-floating-cart { // @at-root: body 직속(pagePopups)으로 렌더되므로 step2-wrap 스코프 밖 전역 규칙으로 컴파일
@at-root .s2-floating-cart {
position: fixed; position: fixed;
bottom: 80px; bottom: 80px;
right: 40px; right: 40px;
@@ -4114,7 +4115,8 @@ input[type="checkbox"]:checked+.custom-checkbox {
} }
// ── Selected APIs Modal ── // ── Selected APIs Modal ──
.s2-modal { // @at-root: body 직속(pagePopups)으로 렌더되므로 step2-wrap 스코프 밖 전역 규칙으로 컴파일
@at-root .s2-modal {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
@@ -150,7 +150,7 @@
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:inline="javascript"> <script th:inline="javascript">
$(document).ready(function () { $(document).ready(function () {
var successMsg = [[${success}]]; var successMsg = [[${ success }]];
if (successMsg) { if (successMsg) {
customPopups.showAlert(successMsg); customPopups.showAlert(successMsg);
} }
@@ -176,7 +176,10 @@
</div> </div>
</div> </div>
</div> </div>
</th:block>
<!-- 화면 전체 오버레이/플로팅은 body 직속(pagePopups)으로 렌더 → wrapper transform·overflow 영향 없이 뷰포트 기준 중앙 정렬 -->
<section layout:fragment="pagePopups">
<!-- Floating Cart Button --> <!-- Floating Cart Button -->
<button type="button" class="s2-floating-cart" id="floatingCartBtn" style="display: none;"> <button type="button" class="s2-floating-cart" id="floatingCartBtn" style="display: none;">
<span class="s2-cart-label">선택된 API</span> <span class="s2-cart-label">선택된 API</span>
@@ -204,7 +207,7 @@
</div> </div>
</div> </div>
</div> </div>
</th:block> </section>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:inline="javascript"> <script th:inline="javascript">
@@ -614,7 +617,7 @@
// Open modal // Open modal
function openModal() { function openModal() {
selectedApisModal.style.display = 'block'; selectedApisModal.style.display = 'flex'; // .s2-modal은 flex 중앙정렬 → block 금지
// Add show class to backdrop and modal for visibility // Add show class to backdrop and modal for visibility
setTimeout(function() { setTimeout(function() {
if (modalOverlay) { if (modalOverlay) {
@@ -663,7 +666,7 @@
// Close modal on ESC key // Close modal on ESC key
document.addEventListener('keydown', function(e) { document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && selectedApisModal.style.display === 'block') { if (e.key === 'Escape' && selectedApisModal.style.display === 'flex') {
closeModal(); closeModal();
} }
}); });
@@ -174,7 +174,10 @@
</div> </div>
</div> </div>
</div> </div>
</th:block>
<!-- 화면 전체 오버레이/플로팅은 body 직속(pagePopups)으로 렌더 → wrapper transform·overflow 영향 없이 뷰포트 기준 중앙 정렬 -->
<section layout:fragment="pagePopups">
<!-- Floating Cart Button --> <!-- Floating Cart Button -->
<button type="button" class="s2-floating-cart" id="floatingCartBtn" style="display: none;"> <button type="button" class="s2-floating-cart" id="floatingCartBtn" style="display: none;">
<span class="s2-cart-label">선택된 API</span> <span class="s2-cart-label">선택된 API</span>
@@ -202,7 +205,7 @@
</div> </div>
</div> </div>
</div> </div>
</th:block> </section>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script th:inline="javascript"> <script th:inline="javascript">
@@ -569,7 +572,7 @@
// Open modal // Open modal
function openModal() { function openModal() {
selectedApisModal.style.display = 'block'; selectedApisModal.style.display = 'flex'; // .s2-modal은 flex 중앙정렬 → block 금지
// Add show class to backdrop and modal for visibility // Add show class to backdrop and modal for visibility
setTimeout(function() { setTimeout(function() {
if (modalOverlay) { if (modalOverlay) {
@@ -618,7 +621,7 @@
// Close modal on ESC key // Close modal on ESC key
document.addEventListener('keydown', function(e) { document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && selectedApisModal.style.display === 'block') { if (e.key === 'Escape' && selectedApisModal.style.display === 'flex') {
closeModal(); closeModal();
} }
}); });
@@ -76,16 +76,6 @@
<!-- Logout State (Authenticated) --> <!-- Logout State (Authenticated) -->
<div class="auth-group authenticated" sec:authorize="isAuthenticated()"> <div class="auth-group authenticated" sec:authorize="isAuthenticated()">
<div class="header-user-info"> <div class="header-user-info">
<span class="session-timer" id="sessionTimer" title="남은 세션 시간">
<i class="fas fa-clock session-timer-icon"></i>
<span class="session-timer-text" id="sessionRemainingTime">--:--</span>
</span>
<!-- 세션 유지(타임아웃 무시) 체크박스: 비운영 + property 활성 시에만 렌더 (prod 미노출) -->
<label class="session-keepalive" th:if="${sessionKeepAliveAllowed}" title="세션 자동 유지 (비운영 전용)">
<input type="checkbox" id="sessionKeepAliveToggle">
<span>세션 유지</span>
</label>
<span class="divider"></span>
<div class="user-identity"> <div class="user-identity">
<img th:src="@{/img/user_icon.svg}" alt="User" class="user-icon"> <img th:src="@{/img/user_icon.svg}" alt="User" class="user-icon">
<span class="user-name">[[${#authentication.principal.userName}]]님</span> <span class="user-name">[[${#authentication.principal.userName}]]님</span>
@@ -140,6 +130,19 @@
</div> </div>
</header> </header>
<!-- 우측 상단 Float 세션 타이머 (인증 사용자, PC 전용). 모바일에서는 CSS로 숨김 -->
<div class="session-float" sec:authorize="isAuthenticated()">
<span class="session-timer" id="sessionTimer" title="남은 세션 시간">
<i class="fas fa-clock session-timer-icon"></i>
<span class="session-timer-text" id="sessionRemainingTime">--:--</span>
</span>
<!-- 세션 유지(타임아웃 무시) 체크박스: 비운영 + property 활성 시에만 렌더 (prod 미노출) -->
<label class="session-keepalive" th:if="${sessionKeepAliveAllowed}" title="세션 자동 유지 (비운영 전용)">
<input type="checkbox" id="sessionKeepAliveToggle">
<span>세션 유지</span>
</label>
</div>
<!-- Mobile Drawer/Modal --> <!-- Mobile Drawer/Modal -->
<div class="mobile-drawer" id="mobileDrawer"> <div class="mobile-drawer" id="mobileDrawer">
<div class="drawer-overlay" id="drawerOverlay"></div> <div class="drawer-overlay" id="drawerOverlay"></div>