져니의 개발 정원 가꾸기

Spring Boot에서 Robots.txt를 추가했는데 왜 적용이 안될까? (feat. 스프링 MVC 자동구성) 본문

개발노트/Spring | Java

Spring Boot에서 Robots.txt를 추가했는데 왜 적용이 안될까? (feat. 스프링 MVC 자동구성)

전전쪄니 2024. 2. 18. 23:58

목차

    배경

    Spring Boot 프레임워크로 개발된 운영 프로젝트 중에서 "사이트 페이지 전체에 대해 인증된 회원들만 접근할 수 있다"라는 요구사항을 가진 서비스가 있어, robots.txt를 통해 검색엔진이 사이트의 정보를 수집/크롤링하지 못하도록 robots.txt를 적용할 필요가 있었다. 

    많은 블로그에서 기본 정적리소스 폴더에 robots.txt만 추가해도 적용되어 [도메인 url 최상단]/robots.txt 경로로 접근 가능하다고 하지만, 사실상 spring boot 구성 설정이 어떻게 되어 있는가에 따라서 파일만 추가하는 것으로 적용되지 않는다. 이에 설정에 따라 robots.txt를 적용하는 방법에 대해 알아보고자 한다.

    Robots.txt

    robots.txt는 뭐하는 애일까?

    robots.txt는 웹사이트에서 크롤링하며 정보를 수집하는 검색엔진 크롤러(또는 검색 로봇)가 액세스 하거나 정보수집을 해도 되는 페이지가 무엇인지, 해서는 안 되는 페이지가 무엇인지 알려주는 역할을 하는 .txt (텍스트) 파일이다. (출처 - robots.txt 10분 안에 끝내는 총정리 가이드). 

     

    마치 접근금지 표지판처럼 robots.txt는 검색엔진 크롤러에게 액세스가 허용되지 않는 디렉토리를 알려주어 해당 디렉토리에 대한 크롤링을 방지하는 일을 한다.

     

    robots.txt를 적용하면 다음과 같은 이점이 있다.

    1. 검색엔진 크롤러의 불필요한 크롤링 및 요청 감소
      • 크롤러의 과도한 크롤링으로 인한 과부하 방지
    2. 크롤링 예산(crawl budget : 검색엔진 크롤러의 일일 요청 수) 낭비 방지
    3. 사이트맵(sitemap.xml)의 위치를 제공하여 검색엔진 크롤러가 웹사이트 콘텐츠를 더 잘 발견하고 수집할 수 있도록 함.
      • 웹 마스터 도구(ex. 구글 search console, 네이버 서치어드바이저)를 이용해 사이트맵을 검색엔진에 제출하지 않고도 robots.txt에 사이트맵 디렉토리를 언급하여 사이트맵이 다른 검색엔진 크롤러에게 빠르게 발견될 수 있게 한다. 
    User-agent: *
    Allow: /
    
    Sitemap: https://www.example.com/sitemap.xml

    robots.txt 작성법

    아래 링크들을 참고하면 목적에 따라 간단한 robots.txt을 작성할 수 있다.

    Spring Boot 에서 적용하기

    그러면 Spring Boot 프레임워크로 개발된 프로젝트에서 robots.txt를 적용하려면 어떻게 해야할까? robots.txt를 적용하는 방법에 대해 chatgpt(3.5 기준)나 구글에 질의해보면 src/main/resources/static 폴더src/main/resources/public 폴더 하위에 robots.txt 파일을 생성하면 적용된다고 한다. 그런데 내 프로젝트가 spring boot 자동구성을 사용하지 않는다면 robots.txt를 생성하는 것만으로는 robots.txt를 적용되지 않는다. 

    WebMvcAutoConfiguration (자동구성인 경우)

    먼저 Spring Boot에서 자동구성을 사용할 경우 어떻게 되는지 보자. 일반적으로 spring boot 프로젝트를 만들 때 main 클래스에 @SpringBootApplication 애너테이션을 단다. 이 애너테이션을 따라가보면 아래 코드에서 보다시피 @EnableAutoConfiguration을 사용하고 있는 것을 확인할 수 있다. @SpringBootApplication을 을 선언하면 다른 설정을 하지 않았다는 전제하에 spring boot는 자동구성(WebMvcAutoConfiguration)을 사용한다.

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
    
        ...
    
    }

     

    자동구성에서는 기본 리소스 경로를 어떻게 관리하고 있는지 보자. @EnableAutoConfiguration 가 정의된 클래스 파일이 속한 org.springframework.boot.autoconfigure 패키지에서 리소스 경로가 어떻게 설정되어있는지 보면 chatgpt(3.5 기준)나 구글에서 알려준  src/main/resources/static 폴더와 src/main/resources/public폴더를 포함한 4가지 경로리소스 경로로 등록하여 사용한다. 그래서 gpt에서 알려준대로 robots.txt 만 추가해도 잘 적용되었던 것이다.


    프로젝트가 자동구성을 사용한다면 네 개의 경로 중 하나의 하위에 robots.txt를 추가한 뒤 잘 적용되었는지 확인하려면  [도메인]/robots.txt 로 접근하면 된다. 404가 뜨지않고 파일에 작성했던 내용이 확인이 가능하면 잘 적용된 것이다.


    코드 :  https://github.com/spring-projects/spring-boot/blob/4fd0e29cc8719bda72e0e67c2392fd48e7b4009d/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/WebProperties.java#L88

     

    WebMvcConfigurationSupport (자동구성이 아닌 경우)

    자동구성을 사용하지 않는 경우는 어떨까? 아래 코드 4번째 줄을 보면 spring boot가 WebMvcConfigurationSupport을 찾을 수 없을 때(사용하지 않을 때) WebMvcAutoConfiguration을 사용한다고 한다. 

    @Configuration
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
    		ValidationAutoConfiguration.class })
    public class WebMvcAutoConfiguration {
    }

     

    그러면 Spring Boot는 WebMvcConfigurationSupport를 언제 사용할까?

    아래 애너테이션 조합은  WebMvcConfigurationSupport을 사용하는 대표적인 예이다.

    • ex. @Configuration + @EnableWebMvc

    spring boot가 등장해 자동구성을 지원하기 전까지 spring에서는 @Configuration와 @EnableWebMvc를 같이 선언하여 설정을 자동화했다. @EnableWebMvc 코드를 보면 DelegatingWebMvcConfiguration을 import하는데 DelegatingWebMvcConfiguration이 WebMvcConfigurationSupport을 상속하고 있어서 @EnableWebMvc를 사용하면 자동으로 WebMvcConfigurationSupport를 사용하게 된다.

     

    그래서 spring boot에서 WebMvcAutoConfiguration 자동구성을 기본으로 사용하더라도, @Configuration + @EnableWebMvc을 사용하면 WebMvcConfigurationSupport가 활성화되어 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) 조건에 따라 WebMvcAutoConfiguration을 사용하지 않는다.

    *
     * @author Dave Syer
     * @author Rossen Stoyanchev
     * @since 3.1
     * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer
     * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
     * @see org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(DelegatingWebMvcConfiguration.class)
    public @interface EnableWebMvc {
    }

     

    @Configuration(proxyBeanMethods = false)
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    
        private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
        
        @Autowired(required = false)
        public void setConfigurers(List<WebMvcConfigurer> configurers) {
            if (!CollectionUtils.isEmpty(configurers)) {
                this.configurers.addWebMvcConfigurers(configurers);
            }
        }
    
        ...
    
        @Override
        @Nullable
        protected MessageCodesResolver getMessageCodesResolver() {
            return this.configurers.getMessageCodesResolver();
        }
    }

     

    그런데 기본적으로 4개의 리소스 폴더를 추가해서 사용하는 WebMvcAutoConfiguration (spring boot의 자동구성)과는 달리 WebMvcConfigurationSupport는 구성 리소스 폴더가 있는 부분을 매핑해주는 설정이 필요하다.

     

    DelegatingWebMvcConfiguration에서 WebMvcConfigurer타입의 빈들을 WebMvcConfigurerComposite 타입의 객체에 주입하는데 이 빈들이 web 관련 빈들을 초기화할 때 사용된다. 따라서 WebMvcConfigurer를 구현하는 리소스 설정을 만들어 매핑 설정을 추가하면 되겠다.

     

    방법1. WebMvcConfigurer를 구현한 리소스 매핑 설정 추가하기

    다음과 같이 새로운 config 클래스를 만들고, addReousrceHandlers()메서드를 오버라이드하여 리소스 매핑을 해주면 된다. 

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        // to-do
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/robots.txt")
                    .addResourceLocations("classpath:/static/robots.txt");
        }
    }

     

    방법2. /robots.txt 요청에 대한 컨트롤러 추가하기

    아래처럼 /robots.txt를 서빙하는 컨트롤러를 생성하는 방법도 있다.

    @Controller
    public class RobotsController {
    
        @RequestMapping("/robots.txt")
        @ResponseBody
        public String robots() {
            return "User-agent: *\n" +
                    "Disallow: / ";
        }
    
    }

    그런데 리소스 매핑 설정을 추가해서 spring boot가 robots.txt 파일을 서빙하는 경우 robots.txt 파일이 <body>태그 아래 <pre> 태그로 예쁘게 감싸지는데, 컨트롤러를 사용하는 경우 pre태그로 감싸지지도 않고, 주석을 노출할 수 없어 브랜드 마크와 같은 것도 보여줄 수 없다.

    리소스 매핑 설정 - <pre>태그로 내용이 감싸짐
    컨트롤러 방식 - pre 태그 없이 내가 입력한 텍스트대로 노출

    개인적으로 방법1로 robots.txt를 제공하는 방식이 사용자에게 보여지는데 더 자연스럽다고 생각한다.

    결론

    사실 이번 블로그는 robots.txt 에 대해서 썼지만, 핵심은 spring boot에서 정적리소스를 어떻게 관리하느냐이다. 원리를 알면 상황에 따라 쉽게 대처할 수 있다. 

    정리 

    • spring boot 자동구성인 경우 (WebMvcAutoConfiguration)
      • 기본으로 설정된 스태틱 경로 중 하나 아래에 파일을 추가하자.
    • spring boot 자동구성이 아닌 경우 (WebMvcConfigurationSupport)
      • 리소스 매핑 설정을 추가하고 설정한 경로 아래에 파일을 추가하자.

     

    참고

    https://seo.tbwakorea.com/blog/robots-txt-complete-guide/    

    https://atoz-develop.tistory.com/entry/How-to-develop-web-app-with-spring-boot-right-away    

    https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-config/static-resources.html 

    https://incheol-jung.gitbook.io/docs/q-and-a/spring/enablewebmvc   

    https://honeymon.io/tech/2018/03/13/spring-boot-mvc-controller.html 

    https://mangkyu.tistory.com/176 

     

    컨트롤러 방법 참고

    https://javaf1.com/how-to/serving-robots-txt-spring-boot