Swagger 바인딩 문제

백종현·2023년 10월 17일
0

똑같은 문제를 새로운 프로젝트를 하면서 겪었는데, 해결법을 확인하러 블로그에 들어왔다. 예전에 블로그에 적은 줄 알았는데 안 적혀 있더라. 이 문제는 항상 자주 발생하는 문제인거 같다.

본론

현재 도커를 활용하여 Spring Server Container 2개, Nginx 컨테이너를 사용하여 서버를 구축하고 있다.

하지만, Swagger에 접속하고 요청을 보내는 경우 endpoint가 http://3.xx.xx.xx/와 같이 우리 서버로 요청이 들어가야 하는데, 이상하게 http://backend로 요청이 가게 되었다. 이를 해결하기 위해 쉽게 Swagger의 설정을 바꾸면 되긴 했지만, 근본적으로 왜 이런 현상이 일어나는지 궁금해졌다.

문제가 생겼을 시점의 Nginx conf 파일이다.

upstream backend {
    server api-server1:8080 weight=2;
    server api-server2:8080 weight=2;
}

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        proxy_pass http://backend;
    }

ServletUriComponetsBuilders의 메소드의 host를 가져오는 부분을 잘 보자.

private static ServletUriComponentsBuilder initFromRequest(HttpServletRequest request) {
   String scheme = request.getScheme();
   String host = request.getServerName();
   int port = request.getServerPort();

   ServletUriComponentsBuilder builder = new ServletUriComponentsBuilder();
   builder.scheme(scheme);
   builder.host(host);
   if (("http".equals(scheme) && port != 80) || ("https".equals(scheme) && port != 443)) {
      builder.port(port);
   }
   return builder;
}

HttpServletRequest에서 getServerName의 설명을 보면, 요청이 보내진 서버의 호스트 이름을 리턴한다고 한다.

  1. Nginx에서 사용한 upstream 서버의 호스트 정보가 아니라, Nginx 서버의 호스트 정보가 반환.
  2. 즉 Request Header에 Host를 Nginx 서버의 호스트 정보로 변경. (proxy_set_header를 사용하지 않는 경우, default로 proxy_pass의 url로 변경되는 듯 하다.)
  3. Tomcat에서 Servlet 객체의 Host 값은 클라이언트가 전송한 요청 헤더의 Host 필드 값으로 당연하게 자동으로 설정되게 된다.
  4. 따라서 Swagger 측에서 ServletUriComponetsBuilders를 default로 만들 때 Servlet 객체에서 getServerName()을 하게 되는데, 이는 Host 값을 가져오게 되고, backend라는 값이 반환되게 된다.
  5. 따라서 swagger 요청이 계속해서 http://backend로 가게 되었던 것이다.

해결 방법

1. Nginx에서 Request Header를 변경

upstream backend {
    server api-server1:8080 weight=2;
    server api-server2:8080 weight=2;
}

server {
    listen       80;
    listen  [::]:80;
		server_name  example.com;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        proxy_pass http://backend;
				proxy_set_header Host $server_name;
    }
}

2. Swagger에서 Docket 객체에 host를 설정하여 변경 (Springfox)

@Bean
public Docket swaggerApi() {
   return new Docket(DocumentationType.SWAGGER_2)
      .host(hostUrl)
	...
}

2. Swagger에서 servers를 설정하여 변경 (Springdocs)

https://stackoverflow.com/questions/60625494/wrong-generated-server-url-in-springdoc-openapi-ui-swagger-ui-deployed-behin

@OpenAPIDefinition( 
    servers = {
       @Server(url = "/", description = "Default Server URL")
    }
) 
@SpringBootApplication
public class App {
    // ...
}

혹은

@Bean
public OpenAPI customOpenAPI() {
    Server server = new Server();
    server.setUrl("https://example.com/api");
    return new OpenAPI().servers(List.of(server));
}

출저 : https://www.vompressor.com/nginx-reverse-proxy/
https://developer88.tistory.com/299

profile
노력하는 사람

0개의 댓글