Skip to content

Commit 87bb0a4

Browse files
committed
fix: 배포 안정성 강화 - 에러 핸들러 연쇄 장애 방지
- ControllerExceptionAdvice: Slack/Sentry 실패 시 try-catch로 감싸서 에러 핸들러 자체가 터지는 연쇄 장애 방지 - SlackApi: StringBuilder를 인스턴스 변수 → 지역 변수로 변경 (스레드 안전) - SlackApi: 빈 스택트레이스 방어 코드 추가 - RecordService: 건강 데이터 조회 실패해도 기록 목록은 정상 반환하도록 try-catch 추가
1 parent 3a6671c commit 87bb0a4

3 files changed

Lines changed: 27 additions & 12 deletions

File tree

src/main/java/org/runnect/server/common/advice/ControllerExceptionAdvice.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.runnect.server.common.advice;
22

33
import io.sentry.Sentry;
4-
import java.io.IOException;
54
import java.util.Objects;
65
import javax.servlet.http.HttpServletRequest;
76
import javax.validation.ConstraintViolationException;
@@ -10,6 +9,7 @@
109
import org.runnect.server.common.dto.ApiResponseDto;
1110
import org.runnect.server.common.exception.BasicException;
1211
import org.runnect.server.config.slack.SlackApi;
12+
import lombok.extern.slf4j.Slf4j;
1313
import org.springframework.http.HttpStatus;
1414
import org.springframework.http.ResponseEntity;
1515
import org.springframework.stereotype.Component;
@@ -21,6 +21,7 @@
2121
import org.springframework.web.bind.annotation.ResponseStatus;
2222
import org.springframework.web.bind.annotation.RestControllerAdvice;
2323

24+
@Slf4j
2425
@RestControllerAdvice
2526
@Component
2627
@RequiredArgsConstructor
@@ -71,9 +72,17 @@ protected ApiResponseDto handleMissingRequestParameterException(final MissingSer
7172
*/
7273
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
7374
@ExceptionHandler(Exception.class)
74-
protected ApiResponseDto<Object> handleException(final Exception error, final HttpServletRequest request) throws IOException {
75-
slackApi.sendAlert(error, request);
76-
Sentry.captureException(error);
75+
protected ApiResponseDto<Object> handleException(final Exception error, final HttpServletRequest request) {
76+
try {
77+
slackApi.sendAlert(error, request);
78+
} catch (Exception e) {
79+
log.error("Slack 알림 전송 실패", e);
80+
}
81+
try {
82+
Sentry.captureException(error);
83+
} catch (Exception e) {
84+
log.error("Sentry 전송 실패", e);
85+
}
7786
return ApiResponseDto.error(ErrorStatus.INTERNAL_SERVER_ERROR);
7887
}
7988

src/main/java/org/runnect/server/config/slack/SlackApi.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ public class SlackApi {
2727
private final static String NEW_LINE = "\n";
2828
private final static String DOUBLE_NEW_LINE = "\n\n";
2929

30-
private StringBuilder sb = new StringBuilder();
31-
3230
public void sendAlert(Exception error, HttpServletRequest request) throws IOException {
3331

3432
List<LayoutBlock> layoutBlocks = generateLayoutBlock(error, request);
@@ -53,15 +51,15 @@ private List<LayoutBlock> generateLayoutBlock(Exception error, HttpServletReques
5351
}
5452

5553
private String generateErrorMessage(Exception error) {
56-
sb.setLength(0);
54+
StringBuilder sb = new StringBuilder();
5755
sb.append("*[🔥 Exception]*" + NEW_LINE + error.toString() + DOUBLE_NEW_LINE);
5856
sb.append("*[📩 From]*" + NEW_LINE + readRootStackTrace(error) + DOUBLE_NEW_LINE);
5957

6058
return sb.toString();
6159
}
6260

6361
private String generateErrorPointMessage(HttpServletRequest request) {
64-
sb.setLength(0);
62+
StringBuilder sb = new StringBuilder();
6563
sb.append("*[🧾세부정보]*" + NEW_LINE);
6664
sb.append("Request URL : " + request.getRequestURL().toString() + NEW_LINE);
6765
sb.append("Request Method : " + request.getMethod() + NEW_LINE);
@@ -71,6 +69,9 @@ private String generateErrorPointMessage(HttpServletRequest request) {
7169
}
7270

7371
private String readRootStackTrace(Exception error) {
72+
if (error.getStackTrace() == null || error.getStackTrace().length == 0) {
73+
return "Unknown";
74+
}
7475
return error.getStackTrace()[0].toString();
7576
}
7677

src/main/java/org/runnect/server/record/service/RecordService.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,15 @@ public GetRecordResponseDto getRecordByUser(Long userId) {
110110

111111
DepartureResponse departure = DepartureResponse.of(course.getDepartureRegion(), course.getDepartureCity());
112112

113-
// 건강 데이터 조회
114-
HealthDataResponse healthData = recordHealthDataRepository.findByRecordId(record.getId())
115-
.map(h -> HealthDataResponse.of(h.getAvgHeartRate(), h.getCalories()))
116-
.orElse(null);
113+
// 건강 데이터 조회 (실패해도 기록 목록은 정상 반환)
114+
HealthDataResponse healthData = null;
115+
try {
116+
healthData = recordHealthDataRepository.findByRecordId(record.getId())
117+
.map(h -> HealthDataResponse.of(h.getAvgHeartRate(), h.getCalories()))
118+
.orElse(null);
119+
} catch (Exception e) {
120+
// 건강 데이터 테이블 미생성 등 예외 발생 시 무시
121+
}
117122

118123
RecordResponse recordResponse = RecordResponse.of(record.getId(), course.getId(), publicCourseId, userId,
119124
record.getTitle(), course.getImage(), record.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")), course.getDistance(), record.getTime().toString(),

0 commit comments

Comments
 (0)