배경
Chat GPT API 를 사용하다보니 꽤나 오랜 시간 응답을 기다려야하는 상황이 필요했다.
오랜 응답을 기다린다는건 어찌보면 내부 토큰이 과하게 사용된거라고 봐도 무방할 정도로, 큰 데이터들 경우 특히나 오래걸렸는데 아니나 다를까 timeout이 발생했다.
이상한 점은 60초가 아닌, 30초에 timeout이 난다는 점이고, 30도 정확하게 30초가 아닌 30.02초, 30.1초 이런식으로 표현됐다.
사실 timeout이라 뜨기보다 499 / 500 에러로 클라이언트가 이미 종료되었거나, 서버내부 에러로 브라우저 콘솔로그에 표시되었지만,
여러번 시도해도 해당 시간에만 종료된다는걸로 보아 타임아웃으로 판단하고 문제해결을 위해 파악하기 시작했다.
해결
우선 다른 환경에서도 동일한지 확인이 필요했다.
기존에 timeout이 뜬 서버를 A서버라고 하고 다른 환경으로 세팅된 서버를 B서버라고 칭하겠다.
두 서버의 역할과 기능은 동일하고, A서버는 B서버 대신 사용하기 위한 리뉴얼 버전의 임시개발서버이다.
확인 결과, timeout 이슈는 B서버에서는 발생하지 않았다.
환경구성 비교해보자면 다음과 같다. ( 대표적인 부분만 표기 )
A 서버
백엔드 | Spring Framwork 6.1 ( JDK : openJDK 17 ) |
프론트엔드 | Next.js ( React , TypeScript ) |
데이터베이스 | Mysql 8.0 |
웹서버 | Nginx |
B 서버
백엔드 | Spring Framework 5 ( JDK : openJDK 11 ) |
프론트엔드 | 백엔드 JSP, HTML 서버 렌더링으로 별도 지정되어있지 않음 |
데이터베이스 | Mysql 5.7 (innoDB) |
웹서버 | Nginx |
Nginx 설정 문제인가?
사실 Nginx 는 리버스프록시를 사용하기 위함이라 내부 설정도 거의 같으므로 무시해도 됐지만, 내부 timeout 옵션들이 있기에 이를 수정하여 해결이 되는지 지켜보기로 했다.
Nginx 에서 각 서버 conf 파일 내부에 Proxy Timeout ( Read, Send, Connection 등) 을 넉넉히 주었다.
결과는 똑같았다.
결론적으로는 Nginx를 거치지 않고 바로 서버들에 다이렉트로 호출하여 시도하였을 때도 현상이 똑같다는걸 확인할 수 있었다.
즉, Nginx는 범인이 아니다.
데이터베이스 문제인가?
데이터베이스는 전혀 상관이 없다. 데이터베이스에 단순 이력을 위한 insert 되는 구간은 있지만, 이는 A서버 B서버 잘 들어오고 있었고
프론트와 백엔드간의 문제이거나 http 통신 관련 이슈일 가능성이 높지 데이터베이스는 전혀 상관이 없다.
데이터베이스는 이번 사건과는 무관하다.
스프링 프레임워크 문제인가?
스프링 프레임워크 6 이전 버전의 경우 다양한 http Request 를 수행하는 방법들이 예시로 나와있는데 이중 대표적으로 RestTemplate 를 과거부터 많이 사용해왔다. 이게 아마 스프링 3부터 있었나 그랬을 거다.
이번 스프링 프레임워크 5 의 경우에도 RestTemplate을 사용해 Request를 수행했는데,
처음엔 RestTemplate 에 timeout이 걸려있을지도 모른다는 생각에 Timeout 설정을 진행해봤지만, 사실 예상대로 별다른 차이는 없었다.
A서버(스프링 프레임워크 6.1)에서도 사실상 동일 코드(RestTemplate)를 사용했기에, 혹여나 스프링의 버전이 올라가면서 해당 기능 내부가 뭔가 바뀌어서 안되는걸지도 모른다는 생각이 들었다.
그래, RestTemplate 말고 다른걸 써보자.
스프링 6에서는 HttpInterface라는걸 권장했다.
변경하다가 너~무~ 자바스럽게도 만들어야하는게 많다보니 그만 뒀다. 귀찮았다. 자바스러운건 너무 싫다.
다음 후보로 6.1부터 권장하는 RestClient 이다.
이건 6.1인 경우에만 사용할 수 있고, 내가 알기로 스프링 부터는 그전부터 쓸 수 있는거로 알 고 있다.
해보니 아주 간단하게 진행했다. RestTemplate의 정신적 계승작 같다. 빌더패턴으로 아주 간결하게 사용할 수 있다.
아주 좋다.
다만, 자바에서 Request를 보내는 쪽은 아무리 바꿔봐도 의미 없었다. 다 timeout이 걸렸다.
스프링프레임워크 버전을 바꿔서 진행해보았지만 똑같았다.
즉, 스프링프레임워크도 범인이 아니다.
프론트엔드 ( Next.js ) 가 문제인가?
A서버의 경우 Next.js 를 사용하고 있다. B의 경우에는 Spring Template를 사용하여 Jsp, Html을 렌더링하고 있다.
Next.js 는 사실 완전한 프론트엔드는 아니고 백엔드적인 요소 및 기능들도 들어가 있는데, 이를 통해 다양한 문제를 해결하고 있기도 하다.
대표적으로 React 에는 없는, Next.js 만의 기능으로 대표적으로 내부 프록시처리 해주는 기능이 있는데 내 경우에는 특정 호스트를 변경하여 특정 요청은 다른 서버를 호출하도록 하는 기능을 수행하고 있다.
이 기능은 Next.js Rewrites 기능이다.
https://nextjs.org/docs/pages/api-reference/next-config-js/rewrites
이번 ChatGPT API 사용 페이지에서도 해당 Rewrites 기능을 사용하고 있었는데,
아니나 다를까...
node_module/next/dist/server/lib/router-utils/proxy-request.js 에 보면
기본 시간이 30초로 되어있다.
주석에 적혀있다싶이 개발환경에서는 30초가 기본이라는데, 그냥.. build 후 start 를 한 경우에도 30초로 박히는걸 확인했다.
잘 설정하라는 뜻인가 보다.
설정하는곳을 찾아보니 숫자 기입하면 되는 것 같다.
이제 잘 설정 해보자.
next.config.js 가 있을텐데, 거기에 사진 또는 아래와 같이 설정한다.
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
proxyTimeout: 90000,
}
// .... 기타 설정
}
테스트 해보니 잘 된다.
범인은 Next.js 였다.
테스트 해보니, 이제 둘다 잘된다.
혹여 나와 같은 이슈로 인해 검색하다가 이 글을 보게 되었고 해결되었다면 댓글 정돈 달아줘라.
이번 이슈를 통해 사이드로 알게된 점
Fetch에는 timeout을 걸 경우 좀 귀찮게 뭔갈 작성해야한다.
반면 Axios는 그냥 옵션으로 제공한다.
결론 : Axios가 여전히 짱짱맨이다.
'이슈' 카테고리의 다른 글
SK하이닉스 SSD P31 펌웨어 업데이트 방법 (0) | 2024.01.02 |
---|---|
AMD 드라이버 시간 초과 발생 (1) | 2024.01.02 |
Jenkins Update 이후 배포 불가 현상시 해결 (0) | 2023.11.16 |
맥에서 파이썬 Pyinstaller 가 안되는 경우 해결 방법 (0) | 2023.10.26 |