ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • User-Agent 정보 가져오기
    웹 개발 2024. 3. 20. 14:54
    반응형

    개발 중에 브라우저 정보를 필요로 하는 요구사항에 따라 습관적으로 구글 검색해서 브라우저 정보를 뽑아오는 코드를 아무생각 없이 작성하였다.

    HttpServletRequest request = ((ServletRequestAttributes)
                    Optional.ofNullable(RequestContextHolder.getRequestAttributes())
                            .orElseThrow(() -> new RuntimeException("request Attribute is null"))
            )
                    .getRequest();
    
            String userAgent = request.getHeader(HEADER_USER_AGENT);
            String browserName = "Unknown";
    
            if (userAgent != null) {
                if (userAgent.contains("MSIE")) {
                    browserName = "Internet Explorer";
                } else if (userAgent.contains("Firefox")) {
                    browserName = "Firefox";
                } else if (userAgent.contains("Chrome")) {
                    browserName = "Chrome";
                } else if (userAgent.contains("Safari")) {
                    browserName = "Safari";
                } else if (userAgent.contains("Opera")) {
                    browserName = "Opera";
                }
            }
    
            return browserName;

     

    위와 같이 작성하였는데..  문제 발생 ㅎㅎ

     

    일단 Edge 브라우저 사용자를 고려하지도 않았고, 

    Edge 브라우저를 고려한다고 해도, 위와 같이 User-Agent 가 되어있는데 .. 정확하게 Edge 를 노출시킬수도 없는 노릇..

     

    Chrome 브라우저는 ..?

     

     

    이렇게 찍혀있다.

    조건문의 순서에 따라 브라우저를 노출 시킬 수도 있겠다싶어 다른 방법들을 찾아보았다.

     

     

     

    Yauaa ( Yet Another UserAgent Analyzer )

    https://yauaa.basjes.nl/ 가 존재했더라니...

     

    사용법을 보면

    이고, Gradle 에는

    implementation 'nl.basjes.parse.useragent:yauaa:7.26.0'

    라고 해줄 수 있다.

    문서에서의 내용에서 주의해야할 점은, UserAgentAnalyzer 의 인스터스를 생성해서 사용해야하는데,
    생성시에 비용이 조금 든다이다. 일반적으로 2~5초 정도가 소요되고, 수백 MiB 의 메모리를 사용하므로 가능한 적은 횟수로 만들어서 사용하라.

    이다.

    그래서 Config 파일로 UserAgentAnalyer 의 빈 생성을 해서 사용시에 빈 주입 후에 사용할까도 싶었는데,, 현재 요구사항으로는 빈번하게 사용될 것 같지 않아. 그때그때 객체를 만들어 사용하기로 했다.

    UserAgentAnalyzer uaa = UserAgentAnalyzer
                .newBuilder()
                .hideMatcherLoadStats()
                .withCache(10000)
                .build();

    를 통해 만든다.

    이후

    UserAgent agent = uaa.parse(/* User-Agent 내용 */)
    
    // 나는 request.getHeader("User-Agent") 를 통해 받아와서 사용
    
    agent.getValue(/* 필요한 필드값 */)

     

    를 통해 사용할 수 있고, getValue 의 필드 이름으로는 AGENT_NAME 이 브라우저의 이름을 가져올 수 있었다.

     

    테스트 해본 결과 

    Firefox / Chrome / Safri / Opera / Edge / Whale 까지 해보고 잘 확인할 수 있었다.

     


     

    OOM(Out Of Memory) 발생!!!

    이후에... 우려하던 OOM 발생이 됬었따..

    결국 객체가 생성되는데 비용이 크다보니 소멸되는데 시간이 걸리고.. 아마 JVM 에서 느리게 GC 처리가 됬나보다..

    OOM 이후에 떨어진 힙덤프 파일에 대한 분석을 하였다.

    Eclipse Memory Analyzer 를 이용했고,

     

    로 보여지고 Problem Suspect2, 3 도 동일하다.

     

    위의 내용으로 3개의 Percentage 가 높은 객체가 userAgent 였다..

     

    해결

    해결로는 UserAgentAnalyer 의 객체가 필요로 될 때 매번 객체가 생성되어 발생한 것이었기에, 빈으로 만든 후에 빈을 주입받아 사용할 수 있게 하였다.

    그래서

    @Configuration
    public class UserAgentConfig {
    
        @Bean
        public UserAgentAnalyzer userAgentAnalyzer() {
            return UserAgentAnalyzer
                    .newBuilder()
                    .hideMatcherLoadStats()
                    .withCache(10000)
                    .build();
        }
    }

    로 만들어서 사용하려고 했으나...

    현재 UserAgentAnalyzer 가 사용되는 클래스는 @UtilityClass 에서 사용되고 있었다.

    아시다시피 @UtilityClass 에서는 정적으로 클래스를 만들어 사용되기 때문에 바램대로 생성, 주입 사용을 할 수가 없었다..

     

    결국 @UtilityClass 에서 정적 초기화 블록을 사용하였다.

    @Slf4j
    @UtilityClass
    public class NetworkUtil {
    	...
        
        private final UserAgentAnalyzer userAgentAnalyzer;
    
        static {
            userAgentAnalyzer = UserAgentAnalyzer
                    .newBuilder()
                    .hideMatcherLoadStats()
                    .withCache(10000)
                    .build();
        }
        
        ...
        
    }

    로 선언해서 메모리에 클래스가 로딩될 때 자동으로 실행되겠고, 초기화 될 떄 한 번만 수행되어 이후에 재사용해서 사용할 수 있게 하였다..

     

     


     

    회고..

    처음보는 라이브러리이기도하고 외부 라이브러리를 사용하는데 당연히 보수적으로 접근했어야했고, 이것 저것 많이 따져봤어야했다..

    너무 안일했었지만, 그래도 이번 기회에 다시 경각심을 가질 수 있었고, 다행히도 빠르게 발견하고 조치할 수 있었기에 잘 넘어갈 수 있었다.

    코드 리뷰에서도 꼼꼼하게 따져보고 넘어갈 수 있도록 해야겠다 느꼈다..

    반응형

    '웹 개발' 카테고리의 다른 글

    Pageable 을 파헤치자.  (39) 2024.04.29
    자바 HashTable 과 HashMap  (0) 2024.03.28
    정적 코드 분석 도구 Sonarqube 도입 제안  (2) 2024.02.24
    @RequestBody, @ResponseBody ??  (2) 2024.01.27
    Flyway  (0) 2022.12.15
Designed by Tistory.