이번 강의에서는 Rq.getActor() 메서드에 점점 쌓여가던 인증 관련 로직을 정리하고,
중복되는 책임을 메서드로 분리하는 리팩토링을 진행했다.
핵심은 인증 로직 자체는 유지하면서, 가독성과 안전성을 높이는 것이다.
기존 getActor() 메서드는 다음과 같은 문제를 가지고 있었다.
req.getHeader()가 null을 반환할 수 있음req.getCookies() 역시 null일 수 있음즉, 동작은 맞지만 유지보수성이 좋지 않은 상태였다.
이번 리팩토링의 목표는 다음과 같다.
null 체크를 Optional로 안전하게 처리getActor()는 “인증 흐름”만 담당하도록 단순화Servlet API의 req.getCookies()는 쿠키가 없을 경우 null을 반환한다.
이를 그대로 사용하면 NPE 위험이 있기 때문에 Optional로 감싸 처리했다.
private String getCookieValue(String name, String defaultValue) {
return Optional
.ofNullable(req.getCookies())
.flatMap(cookies ->
Arrays.stream(cookies)
.filter(cookie -> cookie.getName().equals(name))
.map(Cookie::getValue)
.filter(value -> !value.isBlank())
.findFirst()
)
.orElse(defaultValue);
}
req.getHeader() 역시 값이 없으면 null을 반환한다.
이를 직접 사용하지 않고 전용 메서드로 감쌌다.
private String getHeader(String name, String defaultValue) {
return Optional
.ofNullable(req.getHeader(name))
.filter(headerValue -> !headerValue.isBlank())
.orElse(defaultValue);
}
이제 getActor()에서는 헤더가 있는지 없는지를 직접 신경 쓸 필요가 없다.
리팩토링 이후 getActor()는 인증 흐름만 명확하게 보여주는 코드가 되었다.
public Member getActor() {
String headerAuthorization = getHeader("Authorization", "");
String apiKey;
if (!headerAuthorization.isBlank()) {
if (!headerAuthorization.startsWith("Bearer "))
throw new ServiceException("401-2", "Authorization 헤더가 Bearer 형식이 아닙니다.");
apiKey = headerAuthorization.substring("Bearer ".length()).trim();
} else {
apiKey = getCookieValue("apiKey", "");
}
if (apiKey.isBlank())
throw new ServiceException("401-1", "로그인 후 이용해주세요.");
return memberService
.findByApiKey(apiKey)
.orElseThrow(() -> new ServiceException("401-3", "API 키가 유효하지 않습니다."));
}
HttpServletRequest는 null을 자주 반환한다