문제
- Page Insight 점수가 제대로 측정되지 않고, Google Search Console 에 일부 페이지가 Server Error 로 정상적으로 수집되지 않고 있다는 정보를 전달 받았다.
Page Insight 화면
- 에러 표출
- 한국어로는
필수 traces 수집기가 실행되지 않았습니다.
라는 메세지가 표출
Google Search Console 크롤링 결과 화면
- Page fetch 에
Failed: Server error (5xx)
가 표출되며 정상적으로 크롤링이 이뤄지지 않음
원인 파악
힌트
- 사내 직원 컴퓨터에서는 해당 페이지가 정상적으로 접속됨
- QA 과정도 무사히 통과했음
- 서버상엔 에러 로그가 표출되지 않음
- 어떠한 VoC 도 들어오지 않음
- 아마 이용자는 이 문제에 별로 불편을 느끼지 못할만한 것일 확률이 높음
- 다만, 구글 봇에서는 확실히 500 Error 때문에 페이지를 가져오지 못한다는 결과를 받음
탑다운 방식으로 오류 접근
- 구글 봇과 동일한 환경에서 접근 시도
- 바로 해당 페이지로 접근함
- 비로그인 상태로 접근함
- 스마트폰 환경에서 접근함
구글 봇과 동일한 환경을 이용해 시크릿 모드로 접근해보니 500 Error
와 함께 페이지에 접근되지 않는 현상 확인함
정상 로딩 시 쿠키와 500 Error 시 쿠키가 다르다는 것을 파악함
소스코드 디버그해보기
- 인터셉터나 필터쪽을 먼저 의심함
500 Error
페이지를 뿌려주는 곳은 스프링의 DispatcherServlet 소스코드 내부에 있음- 에러 로그가 없기 때문에 직접 찾아다녀야 함
applyPreHandle()
메서드 까지는 무사히 넘어가는 것 확인DispatcherServlet
내부 메서드인doDispatch()
메서드 중에 Exception 이 던져지는 것을 확인함
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
위 소스코드에서 Exception
으로 넘어가는 것을 확인함
NullPointerException
을 확인했고, stackTrace
의 가장 윗 부분의 소스코드가 문제임을 확인함
문제 메서드 분석
public static HashMap<String, String> getCookie(HttpServletRequest req, String cookieName) {
HashMap<String, String> values = null;
try {
Cookie[] cookies = req.getCookies();
if (cookies != null) {
values = new HashMap<>();
String value;
for (Cookie cookie : cookies) {
String name = cookie.getName();
if (name.equals(cookieName)) {
value = URLDecoder.decode(cookie.getValue(), "UTF-8");
if (value != null && value.contains(";")) {
for (String kv : value.split(";")) {
if (kv != null) {
int idx = kv.indexOf("=");
if (idx > -1)
values.put(kv.substring(0, idx), kv.substring(idx + 1));
}
}
} else {
values.put(name, value);
}
}
}
}
} catch (Exception e) {
logger.error("CookieUtil.getCookie(" + req.getRemoteHost() + ") - catch: " + e.getMessage());
values = null;
}
return values;
}
- 모든 문제의 원흉은 위 메서드의 구조 때문이다.
- 쿠키는 HTTP 통신상의 이유로
String
타입밖에 넣지 못하는데 반환 타입이HashMap<String, String>
이어서 마지막에 API 이용자는getCookie(req, "cookieName").get("cookieName")
을 한번 더 해야하는 기괴한 사용법으로 이용해야 한다. - 오브젝트의
null
체크를 하지 않고Obj.method()
와 같은 형식을 사용하면 NPE 가능성이 있다.- 그래서
null
체크를 항시 해줘야 하는데, 실수하기 쉽다. - 이 경우 차라리 그냥 메서드에서 최대한 해줄 수 있는 만큼은 전부 처리해주는 것이 좋다.
- 이를테면 반환값을 애초에 primitive 타입으로 주거나
String
타입으로 줬으면 문제가 없을 것이다. - 최대한 그렇게 유도해야 한다.
- 이를테면 반환값을 애초에 primitive 타입으로 주거나
- 그래서
문제 해결
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
public class CookieUtil {
/**
* Returns the value of the specified cookie from the request. Returns null if the cookie is not found.
* @param request HttpServletRequest
* @param name Cookie name
* @return Cookie value or null if not found
*/
public static String getCookieValue(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (name.equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
}
String
을 반환하는 쿠키 값을 가져오는 메서드를 새로 추가 후 반영했다.
이후
getCookie()
를 사용한 곳을 찾아getCookieValue()
메서드를 대신 사용하도록 바꾸어주었더니 문제가 전부 해결되었다.
정리
반응형
'회고 > 주간 회고' 카테고리의 다른 글
레디스 코드 변경과 함께 일어났던 인프라 문제 회고 (1) | 2023.05.30 |
---|---|
회사에서 있었던 레디스 코드 대형 리팩토링 회고 (2) | 2023.04.28 |
인텔리제이 톰캣 로그 실종 및 스프링 세션 springSessionRepositoryFilter(DelegatingFilterProxy 타입) 빈을 찾지 못하는 에러에 대한 회고 (0) | 2023.04.24 |
잘못된 도파민 보상 체계를 만들었던 경험 회고 (0) | 2023.04.23 |
스프링 초기 세팅 시 뜬 에러 메세지 관련 회고 (Invalid bean definition with name 'xxx' defined in null) (0) | 2023.04.17 |