Compare commits

49 Commits

Author SHA1 Message Date
이주일
f9b8c8712a fix: 관리자 세션 만료 리다이렉트 루프 방지 2026-07-21 10:34:27 +09:00
이주일
c6c041819b fix: 게시판 연관상품 이미지 필드 통일 2026-07-19 16:05:17 +09:00
이주일
f1767425a0 fix: 게시판 상세 등록일 및 모바일 등록 권한 노출 보정 2026-07-19 14:46:46 +09:00
이주일
cb0157a73b fix: 관리자 게시판 연관상품 이미지 및 로컬 인증 보정 2026-07-19 09:07:52 +09:00
이주일
7564dc21e5 fix: 관리자 게시판 인증 및 저장 오류 처리 2026-07-18 23:32:26 +09:00
이주일
06760482aa merge: develop 게시판 등록 버튼 권한 노출 반영 2026-07-18 15:55:11 +09:00
이주일
1c48d65b7b fix: 관리자 게시판 등록 권한 및 URL 표시 개선 2026-07-18 12:37:10 +09:00
이주일
15f299d842 fix: 추천상품 표시 순서 적용 2026-07-18 12:34:26 +09:00
이주일
49738b87bd fix: 추천상품 표시 설정 및 품절 안내 적용 2026-07-18 12:34:26 +09:00
이주일
1b52e2ecbd fix: 추천상품 PC 카드 목록 구조 통일 2026-07-18 12:34:26 +09:00
이주일
de7bb587e2 fix: 추천상품 카드 표시 기준 보정 2026-07-18 12:34:26 +09:00
이주일
de3c84957d feat: 게시판 연관상품 목록 표준화 2026-07-18 12:34:26 +09:00
이주일
ff289ac2c8 fix: 상품 이미지 업로드 투명도 보존 2026-07-17 14:59:44 +09:00
이주일
19586bd744 feat: 일반게시판 연관상품 노출 지원 2026-07-17 02:49:11 +09:00
이주일
fc47ad11ad fix: 품절 및 판매중지 상품 후순위 정렬 2026-07-16 17:25:47 +09:00
이주일
ad649de088 fix: 매거진 상세 목록 버튼 이동 오류 수정 2026-07-16 17:25:46 +09:00
이주일
c6d656b648 feat: 상품배지설정관리 프로그램 추가 2026-07-16 02:26:31 +09:00
이주일
ac2402d764 feat: 쇼핑몰 상품 배지 마스터 연동 2026-07-16 02:26:31 +09:00
이대환
df8c176dbb fix : 품절 문구 수정 (Sold Out → 입고 대기중) 2026-07-13 16:20:50 +09:00
jazz
846de3009c Revert "fix : 상품상세화면 호출시 title 및 meta 정보에 상품제목 노출"
This reverts commit 194ab35b
2026-07-13 10:10:08 +09:00
jazz
194ab35ba6 fix : 상품상세화면 호출시 title 및 meta 정보에 상품제목 노출 2026-07-10 17:55:32 +09:00
이대환
f894756a5f 미할인 상품 - 판매가, 할인율 미표시
>> 쇼핑몰 메인
>> 카테고리 목록
>> 상품 검색
>> 상품 상세
>> 장바구니 담기 팝업
>> 장바구니
>> 결제 화면
2026-07-10 11:54:17 +09:00
이주일
82ec7f8b20 fix: 운영 주문상세 배송지 수정 오류 보정 2026-07-10 00:29:13 +09:00
jazz
6222c3b7d1 fix : 서치어드바이저 소유권 코드 추가 2026-07-09 18:07:28 +09:00
이대환
184dbbe21b fix : 결제 - 키움결제 네이버페이 CPID 관련처리(모바일) 2026-07-09 14:59:32 +09:00
이대환
2e28122d97 fix: 주문관리 > 전체주문조회 > 발주서 엑셀 다운로드 - 상품단가(과세 USD) 2026-07-09 12:20:33 +09:00
jazz
297664b621 fix : 서치어드바이저 소유권 코드 추가 2026-07-09 10:00:20 +09:00
이대환
c51f716cc4 fix : 주문 관리 > 전체주문조회 > 전체 주문 목록 - 프린트 화폐단위 변경 (원 → 円) 2026-07-08 18:32:08 +09:00
이대환
2b8e84799d fix : 상품 목록 - 스크롤 불러오기 화폐단위 변경 (원 → 円) 2026-07-08 14:11:46 +09:00
이대환
cb655263af fix : 주문 > 반품환불 관리 > 반품환불 처리 > 반품/환불상세 - 네이버페이 주문 제어 제한 2026-07-08 11:32:07 +09:00
이대환
1de8b5c39f fix : 주문 > 주문 관리 > 전체주문조회, 주문 상세 - 네이버페이 주문 제어 제한 2026-07-08 11:21:33 +09:00
이대환
4074ace97d 배송조회(운송장번호 조회) - aciExpress 팝업 적용
>> 쇼핑몰 주문/배송 > 주문 상세
2026-07-08 10:36:59 +09:00
이대환
28ee8f8556 fix : 주문 > 주문 관리 > 전체주문조회, 주문 상세 - 네이버페이 주문 제어 제한 2026-06-29 12:55:14 +09:00
이대환
bbeb2a630a fix : 주문 > 주문 관리 > 전체주문조회 - 네이버페이 주문 제어 제한 2026-06-29 11:23:10 +09:00
이대환
bc7f738e55 fix : 상품관리 > 상품조회 > 상품 등록/복사재등록 - 상품명(영문), HS Code, 실무게(KG) 2026-06-26 18:36:32 +09:00
이대환
32250636e9 fix : 상품관리 > 상품조회 > 상품 등록/복사재등록 - 상품명(영문), HS Code, 실무게(KG) 2026-06-26 18:35:26 +09:00
이대환
6858b24344 주문 > 주문 관리 > 전체주문조회, 주문 상세 - 네이버페이 주문 제어 제한 2026-06-26 18:09:13 +09:00
이대환
c04668fda2 fix : 배송/반품정보 문구 수정 (반품/교환 → 배송/반품) 2026-06-19 14:41:02 +09:00
이대환
1cab83f69f 쇼핑몰 매거진 - 열람 권한 설정(모바일) 2026-06-11 17:42:08 +09:00
이대환
784cadc6f2 쇼핑몰 매거진 - 열람 권한 설정 2026-06-11 17:36:34 +09:00
이대환
39ce46c4dc batch > 출고 상태 변경 2026-06-10 11:41:02 +09:00
이대환
03ec9d863e 쇼핑몰 검색어 저장 2026-06-05 14:44:03 +09:00
이대환
7bfa5a288c fix : 상품 > 상품관리 > 구독서비스 관리 > 구독서비스 삭제 개선, 목록 선택 체크박스 개선 2026-06-02 11:49:55 +09:00
이대환
56f29943b6 fix : 설정 > 원가 설정 - 기준일자 datepicker 적용 2026-06-02 09:47:09 +09:00
이대환
31561200c2 설정 > 원가 설정 추가 2026-06-01 15:49:41 +09:00
이대환
5fa4aa5c37 fix : 설정 > 패밀리사이트 설정 > 입력창 추가 - 스크립트 개선
fix : 상품관리 > 상품조회 > 상품 등록/복사재등록/수정 - 실무게(KG) 자릿수 제한 개선
2026-06-01 15:48:44 +09:00
이주일
f2038728fd fix: FAQ 상세 조회 Ajax Content-Type 추가 2026-06-01 09:56:19 +09:00
이대환
450e43aecb 주문관리 > 전체주문조회 > 엑셀 다운로드, 다운로드 항목설정 - 상품명(영문), HS Code, 실무게, 부피무게, 포장개수 추가 2026-05-29 15:19:19 +09:00
이대환
1d72e28600 쇼핑몰 상품상세, 주문결제화면 - 예상 적립 마일리지 관련
fix : 쇼핑몰 회원정보수정 - 취소 버튼
fix : 쇼핑몰 회원탈퇴 - 취소 버튼, 주요 혜택
2026-05-29 09:51:04 +09:00
10 changed files with 72 additions and 20 deletions

View File

@@ -912,6 +912,7 @@ public class BOGnrlbbscttService {
throw new WnbtRestException(wre.getMessage());
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new WnbtRestException("게시글 등록 중 오류가 발생했습니다.", e);
}
return message;
@@ -1129,6 +1130,7 @@ public class BOGnrlbbscttService {
throw new WnbtRestException(wre.getMessage());
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new WnbtRestException("게시글 수정 중 오류가 발생했습니다.", e);
}
return message;

View File

@@ -197,11 +197,12 @@ public class MngrLoginController {
log.debug("리프레쉬 토큰이 없는 경우 realm :: {}", realm);
//엑세스토큰 삭제
CookieUtils.deleteCookie("ACT","/"+realm, request, response);
CookieUtils.deleteCookie("token_expire","/"+realm, request, response);
CookieUtils.deleteCookie("ACT","/", request, response);
CookieUtils.deleteCookie("RFT","/" + realm + "/tokens/refresh", request, response);
CookieUtils.deleteCookie("token_expire","/", request, response);
response.sendRedirect("/"+realm);
return;
}
//리프레쉬 토큰 있을 경우(존재한다는 자체가 refresh토큰이 만료된 상태가 아님)

View File

@@ -371,7 +371,9 @@ public class MngrLoggingInterceptor implements HandlerInterceptor {
String cookieValue = agentId + ":" + md5(agentId + ":" + md5(System.currentTimeMillis() + ":" + random.nextInt())).substring(5, 10);
Cookie agentIdCookie = new Cookie("agent_id", cookieValue);
agentIdCookie.setMaxAge(86400 * 365 * 10); // 10년
if (!request.getServerName().startsWith("loc.")) {
agentIdCookie.setDomain(domain);
}
// agentIdCookie.setHttpOnly(true);
agentIdCookie.setPath("/mngr");
// agentIdCookie.setSecure(true);

View File

@@ -51,14 +51,17 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
String accessToken = extractToken(request);
String sysDvCd = RealmContextHolder.getSysDvCdRealm();
String expireChk = request.getParameter("expire");
String tokenExpireCookie = CookieUtils.getCookieValue("token_expire", request);
//사용자이면서 ACT가 없는데 tokenExpire는 있을 경우
//로그인된 사용자지만 엑세스 토큰이 만료되어 다시 재발급 받아야 하는 대상
//expire값은 루프 요청 방지
if("01".equals(sysDvCd) && !StringUtils.hasText(accessToken) && StringUtils.hasText(tokenExpireCookie) && !"p".equals(expireChk)){
//인증 진입점은 만료 쿠키가 남아 있어도 갱신 대상으로 보내지 않는다.
//쿠키 삭제가 지연되거나 이전 경로의 쿠키가 남아도 로그인 화면까지는 항상 도달해야 한다.
if(!isAuthenticationEndpoint(requestUri, realm)
&& !StringUtils.hasText(accessToken)
&& StringUtils.hasText(tokenExpireCookie)
&& !"p".equals(expireChk)){
response.sendRedirect("/"+realm+"/tokens/refresh?redirect_uri=%s&expire=%s".formatted(URLEncoder.encode(UrlExtractor.getOriginalURL(request), StandardCharset.UTF_8), "p"));
return;
@@ -66,7 +69,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
// accessToken이 있고 refresh Token 요청이 아닌경우
if (StringUtils.hasText(accessToken) && !requestUri.contains("/tokens/refresh")) {
if (StringUtils.hasText(accessToken) && !isAuthenticationEndpoint(requestUri, realm)) {
UserDetails userDetails = customUserDetailService.loadUserByUsername(accessToken);
if (userDetails != null) {
log.debug("user details: {}", userDetails);
@@ -92,6 +95,15 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
}
private boolean isAuthenticationEndpoint(String requestUri, String realm) {
String realmPath = "/" + realm;
return requestUri.equals(realmPath)
|| requestUri.startsWith(realmPath + "/login")
|| requestUri.equals(realmPath + "/logout")
|| requestUri.startsWith(realmPath + "/tokens/refresh");
}
private String extractToken(HttpServletRequest request) {

View File

@@ -44,9 +44,12 @@ public abstract class CookieUtils {
Cookie cookie = new Cookie(name, null);
cookie.setMaxAge(0);
cookie.setPath(path);
cookie.setDomain(originHost);
setCookieDomain(cookie, request, originHost);
cookie.setHttpOnly(true);
response.addCookie(cookie);
// loc 환경에서는 이전에 상위 도메인으로 저장된 쿠키도 함께 삭제한다.
deleteLegacyDomainCookie(name, path, request, response, originHost);
}
public static void createCookie(String name, String value, String path, int maxAge, boolean httpOnly, HttpServletRequest request, HttpServletResponse response) {
@@ -58,10 +61,12 @@ public abstract class CookieUtils {
String originHost = UrlExtractor.getOriginalHost(host);
deleteLegacyDomainCookie(name, path, request, response, originHost);
Cookie cookie = new Cookie(name, value);
cookie.setMaxAge(maxAge);
cookie.setPath(path);
cookie.setDomain(originHost);
setCookieDomain(cookie, request, originHost);
cookie.setHttpOnly(httpOnly);
response.addCookie(cookie);
}
@@ -77,7 +82,7 @@ public abstract class CookieUtils {
Cookie cookie = new Cookie(name, value);
cookie.setPath(path);
cookie.setDomain(originHost);
setCookieDomain(cookie, request, originHost);
cookie.setHttpOnly(httpOnly);
response.addCookie(cookie);
}
@@ -96,4 +101,30 @@ public abstract class CookieUtils {
return Map.of("ACTPath", ACTPath, "RFTPath", RFTPath);
}
private static void setCookieDomain(Cookie cookie, HttpServletRequest request, String domain) {
if (!request.getServerName().startsWith("loc.")) {
cookie.setDomain(domain);
}
}
private static void deleteLegacyDomainCookie(String name, String path, HttpServletRequest request, HttpServletResponse response, String domain) {
if (request.getServerName().startsWith("loc.")) {
addDomainDeleteCookie(name, path, domain, response);
int firstDot = domain.indexOf('.');
if (firstDot > 0) {
addDomainDeleteCookie(name, path, domain.substring(firstDot + 1), response);
}
}
}
private static void addDomainDeleteCookie(String name, String path, String domain, HttpServletResponse response) {
Cookie domainCookie = new Cookie(name, null);
domainCookie.setMaxAge(0);
domainCookie.setPath(path);
domainCookie.setDomain(domain);
domainCookie.setHttpOnly(true);
response.addCookie(domainCookie);
}
}

View File

@@ -2659,7 +2659,7 @@
AND FGI.IMG_KND = '02'
AND FGI.CUT_NB = 1
LIMIT 1
) AS GOODS_IMG_COURS
) AS IMG
,FG.GOODS_NM
,FG.GOODS_ICON_CD AS ICON_CD_NM_LIST
,FG.SRT_DC AS REPRSNT_DC

View File

@@ -486,7 +486,7 @@
</c:when>
<c:otherwise>
<button type="button" class="btn xsm primary ml-5"
onclick="fn_open_modal('#modalDiv','#product-search','/mngr/goods/goodsmngr/selectGoodsSrchPopupForm',{'goodsKndCd':'01'}); return false;">
onclick="selected = []; fn_open_modal('#modalDiv','#product-search','/mngr/goods/goodsmngr/selectGoodsSrchPopupForm',{'goodsKndCd':'01'}); return false;">
검색
</button>
</c:otherwise>

View File

@@ -824,7 +824,9 @@
<i class="icon icon_lock"></i>
</c:if>
</h4>
<c:if test="${bbsInfo.registDtYn eq 'Y'}">
<p class="eventDetail__date"><c:out value="${bbscttInfo.sumRegistDt}"/></p>
</c:if>
</div>
<%-------------------------------%>
<%--첨부 파일 다운로드 영역--%>
@@ -961,7 +963,7 @@
</c:if>
<a class="product-detail" href="javascript:void(0);" onclick="fn_move_goods_dtl('<c:out value="${item.goodsInnb}"/>');">
<spring:eval expression="@environment.getProperty('sopmalfo.common.fileupload.cdnurl')" var="cdnurl"/>
<img src="<c:out value = "${cdnurl}${item.goodsImgCours}"/>"
<img src="<c:out value = "${cdnurl}${item.img}"/>"
onerror="this.src='/pc/images/image_placeholder.png'" alt="<c:out value="${item.goodsNm}"/>">
</a>
</div>

View File

@@ -1254,7 +1254,9 @@
</c:if>
</h4>
<ul class="eventDetail__list">
<c:if test="${bbsInfo.registDtYn eq 'Y'}">
<li><c:out value="${bbscttInfo.sumRegistDt}"/></li>
</c:if>
<li>조회 <c:out value="${bbscttInfo.inqireCnt}"/></li>
<li>
<c:choose>
@@ -1439,7 +1441,7 @@
<a style="cursor:pointer" loading="lazy"
onclick = "fn_move_goods_dtl(<c:out value="${item.goodsInnb}"/>)">
<spring:eval expression="@environment.getProperty('sopmalfo.common.fileupload.cdnurl')" var="cdnurl"/>
<img src="<c:out value = "${cdnurl}${item.goodsImgCours}"/>"
<img src="<c:out value = "${cdnurl}${item.img}"/>"
onerror="this.src='/pc/images/image_placeholder.png'"
alt="<c:out value="${item.goodsNm}"/>">
</a>

View File

@@ -1059,7 +1059,7 @@
</form>
</div>
<%--게시판 등록 사용여부--%>
<c:if test = "${bbsInfo.bbscttRegist eq 'Y' && (bbsInfo.bbsTyCd eq '01' || bbsInfo.bbsTyCd eq '08') && bbsInfo.sknTyCd ne '02'}"><%--갤러리타입 등록 x--%>
<c:if test = "${bbsInfo.bbscttRegist eq 'Y' && resultMap.registAuthResult eq 'Y' && (bbsInfo.bbsTyCd eq '01' || bbsInfo.bbsTyCd eq '08') && bbsInfo.sknTyCd ne '02'}"><%--갤러리타입 등록 x--%>
<div class="infoSection">
<a style="cursor:pointer" class="btn md full theme" id = "regBtn"><spring:message code="button.insert"/>하기</a>
</div>