기존 CookieUtil의 문제점 찾기
코드리뷰 과정에서 대답에 가장 난항을 겪었던 클래스의 리팩토링 과정을 설명하려고 한다.
우선, 기존 코드는 다음과 같았다.
@Slf4j
@Component
public class CookieUtil {
private static String domain;
private CookieUtil() {};
@Value("${bbaegok.root-domain}")
public void setDomain(String domain) {
CookieUtil.domain = domain;
}
public static Optional<Cookie> getCookie(HttpServletRequest request, String name) {
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
if (name.equals(cookie.getName())) {
return Optional.of(cookie);
}
}
}
return Optional.empty();
}
public static void addCookie(HttpServletResponse response, String name, String value, int maxAge) {
ResponseCookie cookie = ResponseCookie.from(name, value)
.path("/")
.domain(domain
.sameSite("Lax")
.httpOnly(true)
.maxAge(maxAge)
.secure(true)
.build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
}
//생략..
위 코드를 보고 이상한점을 찾으면 된다.
사실 찾기는 쉽다. Util클래스임에도 보이는 난데없는 @Value와 setDomain() 메소드(그리고 사용하지 않는 생성자)
위 코드가 만들어진데는 나름의 배경이 있었는데, domain값을 프로퍼티로부터 받아와서 사용하고 싶어 기존 하드코딩되어있던 Util을 바꾸려다가 생긴 문제다. Util은 빈이 아닌데도 @Value라는 주입이 필수인 로직을 짜려고 하니까 에러가 거듭되었다. 그래서 만든 나름의 해결책이 위 방식이였다.
그래서인지 리뷰과정에서 이 클래스는 Bean인가요, Util인가요? 라는 질문을 받았을때 대답하기 어려웠다.
Bean일까 Util일까
빈일까 유틸일까? 사실 그동안 Util함수의 역할을 딱 정의내려본적이 없었다. 그냥 느낌적으로, 마치 라이브러리처럼 재활용성이 높은 간단한 로직들(특히 비즈니스 자체와 관계없는)을 모아놓은 코드뭉치라고 생각했다.
그래서인지 Util은 어디에 사용하나요? 라고 했을때 대답이 어려웠다.
집에와서 다시 생각해보니, 대답은 생각보다 간단했다.
그동안 만들었던 DateUtil, HeaderUtil처럼 하나의 역할을 위해 모여있는 메소드뭉치인데, "인스턴스를 만들지 않는" 특징을 가진것이다. UUID.create()처럼, static메소드를 통해 바로 호출해서 사용한다. 따라서 상태도 가지지 않는디.
이렇게 정의하고 보니 문제가 더 명확했다.
Cookie를 통해 할 역할이 static 메소드로서 명확했기 때문에 처음에 Util로 생성을 했었으나, 도메인값의 변화에 동적으로 대응하기 위해 @Value를 쓰려고 했던것이 화근이였던 것이다. 왜냐면 @Value로 프로퍼티값을 주입하기 위해선 주입을 받아야한다. 즉 인스턴스변수가 되는것이 선행작업이기 때문이다^.^..
그렇다면 이 클래스는 Component로 변화시키는게 답일까?
staticMethod(Util)와 @Component중에 뭐가 더 기능에 맞는 형태일까?
- 타 클래스와의 의존성이 있다면 @Component를 사용한다.
- 일관된 결과를 제공한다면 static method를 사용한다.
- 성능적 측면에선 static method가 유리하다.
위 기준으로 생각해보았다. 그러나 내가 생각하기에, 이 문제는 어느 하나로 정의하기엔 애매했다.. @Value로 의존성이 생기긴 했지만, 사실상 하드코딩해도 상관없는.. “도메인명”을 저장하는 값이기 때문이다. Util에 더 가까운 작업이라고 생각하는데 이 이유 하나로 @Component가 되는게 맞을까? 라는 고민이 생겼다(대부분의 Cookie가 정적메소드를 구현되는게 더 많아서 고민이 됐다)
그래서 고민을 거듭한결과…
Util에 더 알맞는 메소드를 만들기 위해서는, 파라미터 설정 자체가 잘못되었다는 생각이 들었다. 예를들어 이 Util이 다운로드해서 사용한 라이브러리라고 가정해보자. domain이라는 값이 이미 설정되어 변경할 수 없는 Util은 너무나도 이상하다. 그렇다면 Cookie의 메소드를 호출하는곳에서 domain값을 직접 파라미터로 전송하면 더 명확하고 Util의 성격에 더 알맞지 않을까?
따라서 다음과같이 파라미터를 통해 domain을 호출하는 형태로 변경 할 수 있을것이다. 호출부는 빈으로 관리되고 있어, @Value값도 의존성 주입으로 가질 수 있다.
public static void addCookie(HttpServletResponse response, String name, String value, int maxAge, String domain) {
ResponseCookie cookie = ResponseCookie.from(name, value)
.path("/")
.domain(domain)
.sameSite("Lax")
.httpOnly(true)
.maxAge(maxAge)
.secure(true)
.build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
}
호출부를 다음과같은 형태로 변경했다.
현재 해당 Util을 사용하는 클래스들은 @Service 등 컴포넌트로 등록되어있었다(비즈니스로직에서 호출하니까 당연하긴함..)
@Value("${bbaegok.root-domain}")
private String domain;
protected void saveCookie(HttpServletResponse response, HttpServletRequest request, String cookieName, long tokenExpiry, String tokenValue) {
int cookieMaxAge = (int) tokenExpiry / 60;
CookieUtil.deleteCookie(request, response, cookieName, domain);
CookieUtil.addCookie(response, cookieName, tokenValue, cookieMaxAge, domain);
}
하나 헷갈리던건 있었는데, 바로 다음클래스다.
/** OAuth 인증 요청을 쿠키를 통해 저장하고 관리한다.
*/
@Slf4j
public class OAuth2AuthorizationRequestBasedOnCookieRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
인증서버와의 로그인과정에서 인가응답 연계를 위해 사용하는 쿠키를 처리하는 클래스이다. 컴포넌트로 따로 등록해야하나? 했지만, 생각해보니 SecurityConfig에 해뒀었다ㅎㅎ
@Bean
public OAuth2AuthorizationRequestBasedOnCookieRepository oAuth2AuthorizationRequestBasedOnCookieRepository() {
return new OAuth2AuthorizationRequestBasedOnCookieRepository();
}
이 과정을 통해서 Util함수에 대한 명확한 설명을 할 수 있게 되었다(기존엔 약간 그런 느낌~ 정도로 알고있었던것같다.)
개발/운영 서버를 분리하며 하드코딩을 최대한 피하려다보니 @Value를 굉장히 많이 사용하고 있는데, 빈과의 관계를 생각하면서 짤 필요성을 느꼈다. 함부로 남발하면 안되는것도 알게되었고, 또 해결되었다고해서 그냥 넘기면 안되는것도 알게되었다. 코드리뷰의 중요성을 크게 느꼈다...!! 😅
'프로젝트 > 빼곡' 카테고리의 다른 글
빼곡 스토어 출시! 그리고 개선방향(feat.코드리뷰) (1) | 2025.03.08 |
---|---|
Spring 명시적 Null값으로 부분 업데이트(PATCH) 구현하기 (4) | 2025.01.02 |
여러기기에서 로그인, 다중 로그인 Spring에서 구현하기 (4) | 2024.12.20 |
다른 도메인 환경에서 쿠키 셋팅하기(Feat.samesite Cookie) (4) | 2024.12.12 |
Redis로 최근검색어 구현하기 (1) | 2024.11.25 |