- error 페이지 개발 환경용 상세 오류 정보 추가
- 스택 트레이스 및 예외 클래스 노출 로직 구현 - activeProfile 표시 및 스타일 업데이트
This commit is contained in:
@@ -11,6 +11,8 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* Created by Sungpil Hyun
|
||||
@@ -43,6 +45,15 @@ public class PortalErrorController implements ErrorController {
|
||||
modelAndView.addObject("errorTitle", resolveTitle(status));
|
||||
modelAndView.addObject("errorDescription", resolveDescription(status));
|
||||
modelAndView.addObject("errorMessage", resolveErrorMessage(request, status));
|
||||
modelAndView.addObject("activeProfile", resolveActiveProfile());
|
||||
// include-stacktrace=always(로컬 디버깅 프로파일)일 때만 화면에 스택 트레이스까지 노출
|
||||
if (isErrorDetailEnabled()) {
|
||||
Throwable throwable = resolveThrowable(request);
|
||||
if (throwable != null) {
|
||||
modelAndView.addObject("errorException", throwable.getClass().getName());
|
||||
modelAndView.addObject("errorStackTrace", stackTraceAsString(throwable));
|
||||
}
|
||||
}
|
||||
}
|
||||
return modelAndView;
|
||||
}
|
||||
@@ -54,6 +65,37 @@ public class PortalErrorController implements ErrorController {
|
||||
return environment.acceptsProfiles(Profiles.of("prod"));
|
||||
}
|
||||
|
||||
/**
|
||||
* server.error.include-stacktrace=always 인 경우에만 화면에 스택 트레이스를 노출한다.
|
||||
* (로컬 디버깅 프로파일에서만 해당 값을 always 로 설정 → 운영/스테이징에는 노출되지 않음)
|
||||
*/
|
||||
private boolean isErrorDetailEnabled() {
|
||||
String includeStacktrace = environment.getProperty("server.error.include-stacktrace", "never");
|
||||
return "always".equalsIgnoreCase(includeStacktrace);
|
||||
}
|
||||
|
||||
private String resolveActiveProfile() {
|
||||
String[] activeProfiles = environment.getActiveProfiles();
|
||||
if (activeProfiles.length == 0) {
|
||||
return "default";
|
||||
}
|
||||
return String.join(", ", activeProfiles);
|
||||
}
|
||||
|
||||
private Throwable resolveThrowable(HttpServletRequest request) {
|
||||
Object exception = request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
|
||||
if (exception instanceof Throwable) {
|
||||
return (Throwable) exception;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String stackTraceAsString(Throwable throwable) {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
throwable.printStackTrace(new PrintWriter(stringWriter));
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
private String resolveTitle(Object status) {
|
||||
HttpStatus httpStatus = resolveHttpStatus(status);
|
||||
if (httpStatus == null) {
|
||||
|
||||
@@ -150,6 +150,47 @@
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* Dev Error Detail (운영 미노출) */
|
||||
.error-detail-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.error-dev-notice {
|
||||
background-color: #fffaf0;
|
||||
border: 1px solid #f6e05e;
|
||||
border-radius: 8px;
|
||||
padding: 12px 18px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: #975a16;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.error-dev-notice .error-env {
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.error-dev-notice .error-env strong {
|
||||
color: #c05621;
|
||||
}
|
||||
|
||||
/* Stack Trace */
|
||||
.error-stacktrace-text {
|
||||
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
||||
font-size: 12px;
|
||||
color: #742a2a;
|
||||
line-height: 1.5;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
overflow: auto;
|
||||
max-height: 360px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media screen and (max-width: 600px) {
|
||||
.error-container {
|
||||
@@ -219,12 +260,31 @@
|
||||
잠시 후 다시 시도해 주세요.
|
||||
</p>
|
||||
|
||||
<!-- Error Message (서버에서 전달된 에러 메시지가 있을 경우 표시) -->
|
||||
<div th:if="${errorMessage}" class="error-message-box">
|
||||
<!-- 상세 오류 정보 (개발 환경 전용) - 운영(prod)에서는 표시되지 않음 -->
|
||||
<div th:if="${errorMessage}" class="error-detail-wrap">
|
||||
<!-- 운영 미노출 안내 + 현재 환경 -->
|
||||
<p class="error-dev-notice">
|
||||
⚠ 아래 상세 정보는 개발 편의를 위한 것으로,
|
||||
<strong>운영 환경에서는 표시되지 않습니다.</strong>
|
||||
<span class="error-env">현재 환경: <strong th:text="${activeProfile} ?: 'default'">-</strong></span>
|
||||
</p>
|
||||
|
||||
<!-- 오류 메시지 -->
|
||||
<div class="error-message-box">
|
||||
<p class="error-message-label">오류 상세</p>
|
||||
<p class="error-message-text" th:text="${errorMessage}"></p>
|
||||
</div>
|
||||
|
||||
<!-- 스택 트레이스 (include-stacktrace=always 이고 예외가 있을 때만) -->
|
||||
<div th:if="${errorStackTrace}" class="error-message-box">
|
||||
<p class="error-message-label">
|
||||
<span>스택 트레이스</span>
|
||||
<span th:if="${errorException}" th:text="'(' + ${errorException} + ')'"></span>
|
||||
</p>
|
||||
<pre class="error-stacktrace-text" th:text="${errorStackTrace}"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="error-buttons">
|
||||
<!-- Home Button -->
|
||||
|
||||
Reference in New Issue
Block a user