diff --git a/build.gradle b/build.gradle
index ff330c4..90b7ea6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -145,6 +145,14 @@ sourceSets {
configurations {
annotationProcessor
+
+ // WebLogic 배포 시 Tyrus WebSocket 필터(weblogic.websocket.tyrus.TyrusServletFilter)와
+ // 충돌 방지: WAR 에 번들된 Tomcat WsSci 가 javax.websocket.server.ServerContainer 속성을
+ // WsServerContainer 로 등록 → WebLogic Tyrus 필터가 TyrusServerContainer 로 캐스팅하다 실패.
+ // 앱은 WebSocket 미사용이므로 Tomcat WebSocket 모듈 제외.
+ all {
+ exclude group: 'org.apache.tomcat.embed', module: 'tomcat-embed-websocket'
+ }
}
compileJava {
diff --git a/src/main/java/com/eactive/apim/portal/apps/auth/AuthNoticeProperties.java b/src/main/java/com/eactive/apim/portal/apps/auth/AuthNoticeProperties.java
new file mode 100644
index 0000000..44d2c26
--- /dev/null
+++ b/src/main/java/com/eactive/apim/portal/apps/auth/AuthNoticeProperties.java
@@ -0,0 +1,43 @@
+package com.eactive.apim.portal.apps.auth;
+
+import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.Profiles;
+import org.springframework.stereotype.Component;
+
+/**
+ * 인증(이메일/SMS) 테스트 안내 관련 PTL_PROPERTY 접근 래퍼.
+ *
+ *
그룹 {@code Portal}, 키 {@code auth.test-notice.enabled}(true/false). 값이 참이면 인증 요청
+ * 응답에 인증번호를 실어 화면에 노출한다(실제 발송 대신 테스트 확인 용도). 기존 application.yml
+ * {@code portal.test-auth-notice-enabled} 설정을 DB PTL_PROPERTY 로 이전한 것으로,
+ * {@link TwoFactorProperties} 와 동일한 {@code getOrCreateProperty} 패턴을 따른다.
+ *
+ *
prod 프로파일에서는 DB 값과 무관하게 항상 false 를 반환한다(운영 환경 인증번호 노출 금지).
+ * 세션 keepalive 등 다른 비운영 전용 스위치와 동일한 정책이다.
+ */
+@Component
+@RequiredArgsConstructor
+public class AuthNoticeProperties {
+
+ public static final String GROUP = "Portal";
+ public static final String KEY_TEST_NOTICE_ENABLED = "auth.test-notice.enabled";
+
+ private final PortalPropertyService portalPropertyService;
+ private final Environment environment;
+
+ /**
+ * 인증 요청 응답에 인증번호를 실어 UI 에 노출할지 여부(개발/테스트 전용).
+ * prod 환경에서는 property 값과 무관하게 항상 false.
+ */
+ public boolean isTestNoticeEnabled() {
+ if (environment.acceptsProfiles(Profiles.of("prod"))) {
+ return false;
+ }
+ String value = portalPropertyService.getOrCreateProperty(
+ GROUP, KEY_TEST_NOTICE_ENABLED, "true",
+ "인증(이메일/SMS) 요청 시 인증번호를 화면에 표시할지 여부 (true/false, 테스트 전용)");
+ return value != null && "true".equalsIgnoreCase(value.trim());
+ }
+}
diff --git a/src/main/java/com/eactive/apim/portal/apps/user/controller/OrgRegisterController.java b/src/main/java/com/eactive/apim/portal/apps/user/controller/OrgRegisterController.java
index 2fbe817..f58521c 100644
--- a/src/main/java/com/eactive/apim/portal/apps/user/controller/OrgRegisterController.java
+++ b/src/main/java/com/eactive/apim/portal/apps/user/controller/OrgRegisterController.java
@@ -11,6 +11,8 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
+import javax.servlet.http.HttpSession;
+
@Controller
@RequestMapping("/signup/portalOrg")
@RequiredArgsConstructor
@@ -39,17 +41,28 @@ public class OrgRegisterController {
@RequestParam("registrationScenario") String scenario,
@ModelAttribute PortalOrgRegistrationDTO orgDTO,
@ModelAttribute UserAgreementDTO agreementDTO,
+ HttpSession session,
Model model) {
try {
ValidationResponse response;
switch (scenario) {
- case "new":
+ case "new": {
+ // 가입 폼에서 이메일 인증을 마쳤는지 세션으로 검증 (개인 경로와 동일, 클라 hidden 불신)
+ String verifiedEmail = (String) session.getAttribute("signupVerifiedEmail");
+ boolean emailVerified = verifiedEmail != null
+ && verifiedEmail.equalsIgnoreCase(orgDTO.getLoginId());
response = orgRegisterFacade.registerNewOrgUser(
orgDTO,
- agreementDTO);
+ agreementDTO,
+ emailVerified);
+ if (response.isValid() && emailVerified) {
+ session.removeAttribute("signupVerifiedEmail");
+ session.removeAttribute("signupEmailPending");
+ }
break;
+ }
case "retain":
response = orgRegisterFacade.convertToOrgUser(
diff --git a/src/main/java/com/eactive/apim/portal/apps/user/controller/UserRegisterController.java b/src/main/java/com/eactive/apim/portal/apps/user/controller/UserRegisterController.java
index b44e8a4..f3cd2d8 100644
--- a/src/main/java/com/eactive/apim/portal/apps/user/controller/UserRegisterController.java
+++ b/src/main/java/com/eactive/apim/portal/apps/user/controller/UserRegisterController.java
@@ -146,12 +146,13 @@ public class UserRegisterController {
if (invitationToken == null) {
Optional registered = portalUserService.findByLoginId(portalUserRegistrationDTO.getLoginId());
- // 이메일 인증을 마쳐 ACTIVE 로 저장된 경우 → 바로 자동 로그인(2차 인증 없이) 후 메인 이동
+ // 이메일 인증을 마쳐 ACTIVE 로 저장된 경우 → 자동 로그인(2차 인증 없이) 후 가입 완료 페이지 노출
+ // (완료 페이지의 '홈으로' 버튼으로 로그인 상태 그대로 메인 이동)
if (registered.isPresent()
&& PortalUserEnums.UserStatus.ACTIVE.equals(registered.get().getUserStatus())) {
portalUserAuthService.autoLoginAfterSignup(registered.get(), request);
redirectAttributes.addFlashAttribute("message", "회원가입이 완료되었습니다.");
- return "redirect:/";
+ return "redirect:/signup/complete";
}
// 이메일 미인증(READY) → 회원가입 직후 이메일 인증 단계로 이동(기존 흐름 유지)
diff --git a/src/main/java/com/eactive/apim/portal/apps/user/facade/AuthFacadeImpl.java b/src/main/java/com/eactive/apim/portal/apps/user/facade/AuthFacadeImpl.java
index 371f87d..b4ec6b1 100644
--- a/src/main/java/com/eactive/apim/portal/apps/user/facade/AuthFacadeImpl.java
+++ b/src/main/java/com/eactive/apim/portal/apps/user/facade/AuthFacadeImpl.java
@@ -1,5 +1,6 @@
package com.eactive.apim.portal.apps.user.facade;
+import com.eactive.apim.portal.apps.auth.AuthNoticeProperties;
import com.eactive.apim.portal.apps.auth.service.AuthNumberService;
import com.eactive.apim.portal.apps.user.dto.ValidationResponse;
import lombok.RequiredArgsConstructor;
@@ -13,6 +14,7 @@ import org.slf4j.LoggerFactory;
public class AuthFacadeImpl implements AuthFacade {
private final AuthNumberService authNumberService;
+ private final AuthNoticeProperties authNoticeProperties;
private static final Logger log = LoggerFactory.getLogger(AuthFacadeImpl.class);
@@ -44,7 +46,10 @@ public class AuthFacadeImpl implements AuthFacade {
String generatedAuthNumber = authNumberService.sendRequestAuthNumber(recipientKey, msgType);
response.setValid(true);
response.setMessage("인증번호를 발송하였습니다.");
- response.setAuthNumber(generatedAuthNumber); // 테스트 환경에서 인증번호 표시용
+ // 테스트 환경(PTL_PROPERTY auth.test-notice.enabled=true, prod 제외)에서만 인증번호를 응답에 노출
+ if (authNoticeProperties.isTestNoticeEnabled()) {
+ response.setAuthNumber(generatedAuthNumber);
+ }
} catch (Exception e) {
response.setValid(false);
response.setMessage(e.getMessage());
diff --git a/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacade.java b/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacade.java
index b7ac80a..e916f8c 100644
--- a/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacade.java
+++ b/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacade.java
@@ -7,10 +7,11 @@ import org.springframework.http.ResponseEntity;
public interface OrgRegisterFacade {
- // 신규 법인 회원 등록
+ // 신규 법인 회원 등록 (emailVerified: 가입 폼에서 이메일 인증 완료 시 true → 바로 ACTIVE)
ValidationResponse registerNewOrgUser(
PortalOrgRegistrationDTO orgDTO,
- UserAgreementDTO agreementDTO) ;
+ UserAgreementDTO agreementDTO,
+ boolean emailVerified) ;
// 기존 회원의 법인 전환
ValidationResponse convertToOrgUser(
diff --git a/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacadeImpl.java b/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacadeImpl.java
index f79b354..6c5f77c 100644
--- a/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacadeImpl.java
+++ b/src/main/java/com/eactive/apim/portal/apps/user/facade/OrgRegisterFacadeImpl.java
@@ -64,7 +64,8 @@ public class OrgRegisterFacadeImpl implements OrgRegisterFacade {
@Transactional
public ValidationResponse registerNewOrgUser(
PortalOrgRegistrationDTO orgDTO,
- UserAgreementDTO agreementDTO) {
+ UserAgreementDTO agreementDTO,
+ boolean emailVerified) {
if (!agreementValidator.isAgreementAccepted(agreementDTO)) {
return new ValidationResponse(false, "약관에 모두 동의해야 합니다.");
@@ -87,7 +88,7 @@ public class OrgRegisterFacadeImpl implements OrgRegisterFacade {
if (uploadedFile == null) {
return new ValidationResponse(false, "첨부파일이 올바르지 않습니다.");
}
- return registerNewCorporateUser(orgDTO, uploadedFile);
+ return registerNewCorporateUser(orgDTO, uploadedFile, emailVerified);
} catch (IllegalArgumentException | IOException e) {
return new ValidationResponse(false, e.getMessage());
}
@@ -179,7 +180,8 @@ public class OrgRegisterFacadeImpl implements OrgRegisterFacade {
// 신규 법인 사용자 등록 메서드
private ValidationResponse registerNewCorporateUser(
PortalOrgRegistrationDTO orgDTO,
- FileInfo uploadedFile) {
+ FileInfo uploadedFile,
+ boolean emailVerified) {
// 사업자등록번호 중복 체크 (이중 방어)
if (portalOrgService.existsByCompRegNo(orgDTO.getCompRegNo())) {
@@ -190,7 +192,7 @@ public class OrgRegisterFacadeImpl implements OrgRegisterFacade {
PortalOrg newOrg = portalOrgService.registerOrgFromDTOWithFile(orgDTO, uploadedFile);
// 사용자 생성 및 기관 연결
- PortalUser newUser = portalUserService.createUserWithOrg(orgDTO, newOrg, "corporate");
+ PortalUser newUser = portalUserService.createUserWithOrg(orgDTO, newOrg, "corporate", emailVerified);
agreementsFacade.saveUserAgreements(newUser.getId(), AgreementType.PRIVACY_COLLECT);
diff --git a/src/main/java/com/eactive/apim/portal/apps/user/service/PortalUserService.java b/src/main/java/com/eactive/apim/portal/apps/user/service/PortalUserService.java
index 986dd69..7dede1b 100644
--- a/src/main/java/com/eactive/apim/portal/apps/user/service/PortalUserService.java
+++ b/src/main/java/com/eactive/apim/portal/apps/user/service/PortalUserService.java
@@ -175,11 +175,26 @@ public class PortalUserService {
}
public PortalUser createUserWithOrg(PortalUserRegistrationDTO userDTO, PortalOrg org, String registrationType) {
+ return createUserWithOrg(userDTO, org, registrationType, false);
+ }
+
+ /**
+ * @param emailVerified 가입 폼에서 이메일 인증을 이미 완료했으면 true → 바로 ACTIVE 로 저장
+ * (개인 경로 registerActiveUser 와 동일. 법인 신규가입 경로에서 사용)
+ */
+ public PortalUser createUserWithOrg(PortalUserRegistrationDTO userDTO, PortalOrg org, String registrationType, boolean emailVerified) {
PortalUser newUser = new PortalUser();
mapDtoToEntity(newUser, userDTO);
newUser.setPortalOrg(org);
setUserProperties(newUser);
newUser.setUserStatus(UserStatus.READY);
+
+ // 이메일 인증 기능 비활성화 시, 또는 가입 폼에서 이미 인증을 마친 경우 바로 활성화 처리
+ Map propertyMap = portalPropertyService.getPortalPropertiesAsMap("Portal");
+ if (emailVerified
+ || "true".equalsIgnoreCase(propertyMap.getOrDefault("disable_features.user_email_verify", ""))) {
+ newUser.setUserStatus(UserStatus.ACTIVE);
+ }
newUser.setApprovalStatus(ApprovalStatus.COMPLETED);
setUserRole(newUser, registrationType);
return portalUserRepository.save(newUser);
diff --git a/src/main/java/com/eactive/apim/portal/common/breadcrumb/GlobalControllerAdvice.java b/src/main/java/com/eactive/apim/portal/common/breadcrumb/GlobalControllerAdvice.java
index 6f9ab52..933992b 100644
--- a/src/main/java/com/eactive/apim/portal/common/breadcrumb/GlobalControllerAdvice.java
+++ b/src/main/java/com/eactive/apim/portal/common/breadcrumb/GlobalControllerAdvice.java
@@ -9,6 +9,7 @@ import org.springframework.core.env.Profiles;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import com.eactive.apim.portal.config.PortalProperties;
+import com.eactive.apim.portal.apps.auth.AuthNoticeProperties;
import com.eactive.apim.portal.apps.session.service.UserSessionService;
import com.eactive.apim.portal.common.security.ClientGuardService;
import com.eactive.apim.portal.portalproperty.service.PortalPropertyService;
@@ -31,6 +32,9 @@ public class GlobalControllerAdvice {
@Autowired
private PortalPropertyService portalPropertyService;
+ @Autowired
+ private AuthNoticeProperties authNoticeProperties;
+
@Autowired
private Environment environment;
@@ -48,12 +52,12 @@ public class GlobalControllerAdvice {
@ModelAttribute("showTestAuthNotice")
public boolean showTestAuthNotice() {
- return portalProperties.isTestAuthNoticeEnabled();
+ return authNoticeProperties.isTestNoticeEnabled();
}
@ModelAttribute("testAuthNumber")
public String getTestAuthNumber() {
- if (portalProperties.isTestAuthNoticeEnabled()) {
+ if (authNoticeProperties.isTestNoticeEnabled()) {
String authVirtualCode = portalProperties.getAuthVirtualCode();
// 고정 인증번호가 있으면 반환, 없으면 "random" 표시
return (authVirtualCode != null && !authVirtualCode.isEmpty()) ? authVirtualCode : "random";
diff --git a/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationManager.java b/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationManager.java
index d8d65b5..f4eb214 100644
--- a/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationManager.java
+++ b/src/main/java/com/eactive/apim/portal/config/PortalAuthenticationManager.java
@@ -79,6 +79,21 @@ public class PortalAuthenticationManager implements AuthenticationManager {
}
if (user.getPortalOrg() != null) {
+ // 법인은 "정상(ACTIVE)" 상태에서만 로그인 허용 (준비/탈퇴/휴면 차단, 2FA 이전 단계)
+ PortalOrgEnums.OrgStatus orgStatus = user.getPortalOrg().getOrgStatus();
+ if (!PortalOrgEnums.OrgStatus.ACTIVE.equals(orgStatus)) {
+ log.debug("법인 비정상 상태 로그인 차단 - user : {} / org.orgStatus : {}", user.getUsername(), orgStatus);
+ if (PortalOrgEnums.OrgStatus.REMOVED.equals(orgStatus)) {
+ throw new DisabledException("삭제된 법인의 계정입니다. 로그인할 수 없습니다.");
+ }
+ if (PortalOrgEnums.OrgStatus.INACTIVE.equals(orgStatus)) {
+ throw new DisabledException("휴면 상태의 법인입니다. 관리자에게 문의하세요.");
+ }
+ // READY 등 승인 전 상태
+ throw new DisabledException("로그인할 수 없습니다. 관리자에게 문의하세요. (법인 승인대기중)");
+ }
+
+ // 방어적: ACTIVE 인데 승인 미완료인 예외 케이스 차단
if (!user.getPortalOrg().getApprovalStatus().equals(PortalOrgEnums.ApprovalStatus.COMPLETED)) {
log.debug("기업사용자 - getApprovalStatus : {} / - org.approvalStatus : {}", user.getApprovalStatus(), user.getPortalOrg().getApprovalStatus());
throw new DisabledException("로그인할 수 없습니다. 관리자에게 문의하세요. (법인 승인대기중)");
diff --git a/src/main/java/com/eactive/apim/portal/config/PortalConfigWebDispatcherServlet.java b/src/main/java/com/eactive/apim/portal/config/PortalConfigWebDispatcherServlet.java
index 4080179..503d534 100644
--- a/src/main/java/com/eactive/apim/portal/config/PortalConfigWebDispatcherServlet.java
+++ b/src/main/java/com/eactive/apim/portal/config/PortalConfigWebDispatcherServlet.java
@@ -6,6 +6,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import nz.net.ultraq.thymeleaf.layoutdialect.LayoutDialect;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
@@ -26,6 +27,7 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.resource.PathResourceResolver;
import org.springframework.web.servlet.resource.ResourceUrlEncodingFilter;
import org.springframework.web.servlet.resource.VersionResourceResolver;
@@ -68,6 +70,33 @@ public class PortalConfigWebDispatcherServlet implements WebMvcConfigurer {
return new LayoutDialect();
}
+ /**
+ * redirect 응답에 전역 {@code @ModelAttribute}(브레드크럼용 pageName·showTestAuthNotice·
+ * testAuthNumber·sessionTimeoutMinutes 등)가 쿼리스트링으로 노출되는 것을 차단한다.
+ *
+ *
Spring Boot 2.6+ 에서 {@code spring.mvc.ignore-default-model-on-redirect} 프로퍼티가
+ * 제거됐고, 본 애플리케이션은 {@code @EnableWebMvc} 로 Boot 자동설정을 우회하므로
+ * {@link RequestMappingHandlerAdapter} 의 프레임워크 기본값(false)이 적용된다. 그 결과
+ * {@code redirect:} 를 반환하는 모든 컨트롤러에서 {@link com.eactive.apim.portal.common.breadcrumb.GlobalControllerAdvice}
+ * 의 기본 모델 값이 redirect URL 에 append 되어 프론트 전용 파라미터가 주소창에 그대로 드러난다.
+ * 어댑터 생성 이후 플래그만 true 로 뒤집어(Boot/기존 설정은 그대로 유지) 누수를 막는다.
+ *
+ *
{@code static} 메서드로 선언해 이 설정 클래스가 조기 초기화되는 것을 피한다.
+ * 명시적 {@code RedirectAttributes}/flash 속성은 영향받지 않는다(기본 모델만 무시).
+ */
+ @Bean
+ public static BeanPostProcessor ignoreDefaultModelOnRedirectPostProcessor() {
+ return new BeanPostProcessor() {
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) {
+ if (bean instanceof RequestMappingHandlerAdapter) {
+ ((RequestMappingHandlerAdapter) bean).setIgnoreDefaultModelOnRedirect(true);
+ }
+ return bean;
+ }
+ };
+ }
+
// -------------------------------------------------------------
// RequestMappingHandlerMapping 설정 View Controller 추가
// -------------------------------------------------------------
diff --git a/src/main/java/com/eactive/apim/portal/config/PortalProperties.java b/src/main/java/com/eactive/apim/portal/config/PortalProperties.java
index f1306b5..148f79c 100644
--- a/src/main/java/com/eactive/apim/portal/config/PortalProperties.java
+++ b/src/main/java/com/eactive/apim/portal/config/PortalProperties.java
@@ -25,8 +25,6 @@ public class PortalProperties {
private String authVirtualCode = "";
- private boolean testAuthNoticeEnabled = false;
-
private Map> portalSecurity;
private FileProperties file = new FileProperties();
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 88ae6a1..9b819f8 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -40,7 +40,6 @@ gateway:
portal:
# auth-virtual-code: 654321
- test-auth-notice-enabled: true
dev:
# application.yml의 `page:` 트리(브레드크럼/메뉴 이름) 라이브 반영
hot-reload-pages: true
\ No newline at end of file
diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml
index 6a5d06f..9980d52 100644
--- a/src/main/resources/application-prod.yml
+++ b/src/main/resources/application-prod.yml
@@ -28,9 +28,6 @@ spring:
thymeleaf:
cache: false
-portal:
- test-auth-notice-enabled: false # prod 환경: UI 안내 미표시
-
app:
resource-versioning:
enabled: true
diff --git a/src/main/resources/application-stage.yml b/src/main/resources/application-stage.yml
index 79480af..511c4b5 100644
--- a/src/main/resources/application-stage.yml
+++ b/src/main/resources/application-stage.yml
@@ -22,7 +22,6 @@ spring:
cache: false
portal:
- test-auth-notice-enabled: true
# 검증 단계에선 사용하지 않음
# auth-virtual-code: 654321
dev:
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 3ce1a25..1d1d712 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -112,7 +112,6 @@ portal:
auth-ttl: 300
auth:
resend_limit_seconds: 30
- test-auth-notice-enabled: true # 기본값: true (대부분의 개발환경에서 UI 안내 표시)
user-approval: true
password-expiration-days: 90
diff --git a/src/main/resources/templates/views/apps/register/components/commonUserInfoForm.html b/src/main/resources/templates/views/apps/register/components/commonUserInfoForm.html
index 400aab4..c629d25 100644
--- a/src/main/resources/templates/views/apps/register/components/commonUserInfoForm.html
+++ b/src/main/resources/templates/views/apps/register/components/commonUserInfoForm.html
@@ -54,6 +54,8 @@
+
@@ -240,6 +242,12 @@
setEmailAuthMsg('인증번호를 발송했습니다.', false);
btn.text('인증번호 재발송');
startEmailCodeTimer();
+ // 테스트 환경(PTL_PROPERTY auth.test-notice.enabled)일 때만 인증번호 노출
+ if (res.authNumber) {
+ $('#emailTestNotice').html('테스트 환경입니다. 실제 이메일이 발송되지 않으며, 인증번호는 ' + res.authNumber + ' 입니다.').show();
+ } else {
+ $('#emailTestNotice').hide().empty();
+ }
} else {
setEmailAuthMsg((res && res.message) || '발송에 실패했습니다.', true);
}
diff --git a/src/main/resources/templates/views/apps/register/components/orgUserInfoForm.html b/src/main/resources/templates/views/apps/register/components/orgUserInfoForm.html
index e2195a7..1234580 100644
--- a/src/main/resources/templates/views/apps/register/components/orgUserInfoForm.html
+++ b/src/main/resources/templates/views/apps/register/components/orgUserInfoForm.html
@@ -45,6 +45,8 @@
+
@@ -122,8 +124,9 @@
if (emailChangeForm) emailChangeForm.style.display = 'none';
// 중복체크 통과 → 이메일 인증 UI 노출
+ // (block 지정 시 .org-form-group 의 display:flex 를 덮어써 라벨/입력이 세로로 깨짐 → '' 로 CSS flex 복원)
var evRow = document.getElementById('emailVerifyRow');
- if (evRow) evRow.style.display = 'block';
+ if (evRow) evRow.style.display = '';
break;
case "conversionOrChange":
@@ -296,6 +299,17 @@
setEmailAuthMsg('인증번호를 발송했습니다.', false);
btn.textContent = '인증번호 재발송';
startEmailCodeTimer();
+ // 테스트 환경(PTL_PROPERTY auth.test-notice.enabled)일 때만 인증번호 노출
+ var notice = document.getElementById('emailTestNotice');
+ if (notice) {
+ if (res.authNumber) {
+ notice.innerHTML = '테스트 환경입니다. 실제 이메일이 발송되지 않으며, 인증번호는 ' + res.authNumber + ' 입니다.';
+ notice.style.display = '';
+ } else {
+ notice.style.display = 'none';
+ notice.innerHTML = '';
+ }
+ }
} else {
setEmailAuthMsg((res && res.message) || '발송에 실패했습니다.', true);
}
diff --git a/src/main/resources/templates/views/apps/register/userRegisterResult.html b/src/main/resources/templates/views/apps/register/userRegisterResult.html
index 6010a6c..fd2848c 100644
--- a/src/main/resources/templates/views/apps/register/userRegisterResult.html
+++ b/src/main/resources/templates/views/apps/register/userRegisterResult.html
@@ -24,19 +24,15 @@
-