IPv6를 사용한 API 연결

API connection via ipv6 vs. ipv4 - support - Discourse Meta 후속 질문

최근 Node.js를 18에서 20으로 업그레이드했는데, 갑자기 내 애플리케이션이 Discourse로부터 API 응답을 받지 못하게 되었습니다. 위에서 언급한 이슈와 관련이 있다는 것을 파악하는 데 시간이 좀 걸렸습니다. IPv4로만 연결하도록 설정하여 이를 확인했습니다:

import {Agent} from 'node:https'
import axios from 'axios'
import {env} from 'node:process'
export const axiosDiscourse = axios.create({
  baseURL: 'https://<discourse-url>',
  headers: {
    'api-key': env['DISCOURSE_KEY'],
    'api-username': env['DISCOURSE_USERNAME']
  },
  httpsAgent: new Agent({
    family: 4
  })
})

중요한 부분은 HTTP Agent를 지정하는 것입니다. 이전에 이 옵션 없이 사용했습니다. 이제 이렇게 하면 작동하지만, Axios를 다른 fetch 기반 라이브러리(예: Wretch)로 대체하는 것을 시도 중이었습니다. (주요 목적은 프론트엔드에서도 사용하여 번들 크기를 줄이는 것입니다): elbywan/wretch: A tiny wrapper built around fetch with an intuitive syntax. :candy: (github.com). 그러나 fetch에서는 IPv4 또는 IPv6를 지정할 수 있는 옵션이 없습니다. 따라서 다음과 같은 두 가지 방법 중 하나를 사용해야 한다고 생각했습니다:

  1. 어떤 식으로든 IPv4만 사용하도록 강제하는 것. 지금까지 여러 시도를 해보았지만 성공하지 못했습니다.
  2. Discourse 측에 IPv6에서 이 문제를 해결하는 방법에 대해 문의하는 것.

내 애플리케이션에서는 다양한 다른 API에 연결하고 있지만, 이 문제는 Discourse 연결에서만 발생합니다.

도움이 될 경우를 대비해 발생한 오류를 공유합니다:

Error: connect ETIMEDOUT 184.105.99.43:443
    at createConnectionError (node:net:1634:14)
    at Timeout.internalConnectMultipleTimeout (node:net:1685:38)
    at listOnTimeout (node:internal/timers:575:11)
    at processTimers (node:internal/timers:514:7) {
  errno: -110,
  code: 'ETIMEDOUT',
  syscall: 'connect',
  address: '184.105.99.43',
  port: 443
},
Error: connect ENETUNREACH 2602:fd3f:3:ff01::2b:443 - Local (:::0)
    at internalConnectMultiple (node:net:1176:40)
    at Timeout.internalConnectMultipleTimeout (node:net:1687:3)
    at listOnTimeout (node:internal/timers:575:11)
    at processTimers (node:internal/timers:514:7) {
  errno: -101,
  code: 'ENETUNREACH',
  syscall: 'connect',
  address: '2602:fd3f:3:ff01::2b',
  port: 443
}

흥미로운 점은, 위에서 언급한 스레드와 달리 긴 시간 후에 응답이 오는 것이 아니라, 제 요청은 즉시 이 오류로 실패한다는 것입니다. 브라우저, curl, 그리고 테스트할 수 있는 다른 모든 도구에서는 API 호출이 정상적으로 작동합니다.

제 애플리케이션에서 요청을 보내는 방식은 중요하지 않습니다. 위에서 언급한 family 옵션을 사용하는 경우를 제외하고는 단순히 실패할 뿐입니다. 링크된 스레드에서 다음과 같은 언급을 볼 수 있습니다:

네트워크에서 IPv6 연결이 작동하지 않는 이유를 조사해야 합니다.

이 부분에 대해 두 가지 우려가 있습니다:

  1. 이를 구체적으로 어떻게 확인해야 하는지 모르겠습니다.
  2. 왜 이 문제가 Discourse와 제 애플리케이션에서만 발생하는지 확실하지 않습니다.

한 가지 더 참고할 점은, 이 문제가 내 WSL 인스턴스 안에서만 발생한다는 것입니다. 호스트 Windows 디바이스에서 애플리케이션을 직접 실행하면 정상적으로 연결됩니다.

이것이 내 환경 설정에 매우 특이한 문제일 것이라 생각하지만, 지금은 어디서부터 문제를 찾아야 할지 막막합니다. 어떤 방향으로 살펴봐야 할지 힌트를 주시면 감사하겠습니다!

사용 중인 라이브러리가 IPv4와 IPv6를 모두 통해 연결을 시도하고 있는 것으로 보입니다. 이는 좋은 소식이며, 여기에서 겪는 것과 같은 문제가 없다는 뜻입니다.

각 문제를 별도로 다뤄보겠습니다:

이는 사용자의 측에서 호스팅된 인스턴스에 연결을 시도하기 위해 TCP SYN을 설정했지만 응답을 받지 못했다는 것을 의미합니다. 일반적으로 다음 중 하나를 의미합니다:

  • 요청이 서버까지 도달하지 못함
  • 서버(또는 경로상의 어떤 요소)가 의도적으로 요청을 조용히 드롭함
  • 응답이 사용자의 컴퓨터로 돌아오지 못함

이 문맥을 고려할 때, 사용자의 컴퓨터 네트워크 설정에 문제가 있을 가능성이 높습니다.

이는 네트워크 스택이 "거기까지 도달할 수 없습니다"라고 보고한 것이며, 사용 중인 도구가 분명히 IPv4로 폴백(fallback)하기 때문에 문제없습니다.

전체 상황을 살펴보면, 다음과 같은 추측이 가능합니다:

  • 사용자의 컴퓨터에서 IPv4가 실패함 (이것이 사실인지 명확하지 않으므로 확인해 주세요 — "정상적으로 연결됩니다"라는 말은 IPv4 또는 IPv6 중 하나를 사용하고 있다는 뜻일 수 있습니다)
  • WSL 컨테이너에서 IPv4가 실패함
  • 사용자의 컴퓨터에서 IPv6는 작동함
  • WSL 컨테이너에서 IPv6가 실패함

@team에게 외부 IPv4 주소를 메모로 보내주시면 의도적으로 드롭되는지 확인해 드리겠습니다. 일부 행동이 불량한 IP 범위가 차단되어 있을 수 있으며, 이를 통해 해당 가능성을 배제할 수 있습니다.

그러나 사용자의 컴퓨터에서는 IPv4와 IPv6 모두 작동하지만 WSL 컨테이너에서는 실패한다면, 이는 WSL 설정과 관련하여 사용자가 직접 해결해야 할 문제입니다.

답장 주셔서 정말 감사합니다.

의심되는 항목들에 대해 지금까지 테스트해 본 내용은 다음과 같습니다:

  • Windows 설정에서 IPv6을 비활성화해 보았습니다:

제 이해로는 이렇게 하면 IPv4로만 연결이 이루어져야 합니다. 실제로도 그렇게 작동했습니다.

  • WSL에서 다음 명령어를 실행해 보았습니다:
 curl -4 https://<host>/latest.json?order=created

정상적으로 작동했습니다(응답을 받았습니다).

  • IPv6만 테스트하는 방법은 확실하지 않습니다. 위 스크린샷과 유사하게 IPv4를 비활성화하고 IPv6만 켜두면, 제 PC가 인터넷에서 완전히 단절됩니다. 모든 연결이 실패했는데, 이는 예상 가능한 결과입니다.

  • WSL에서 다음 명령어를 실행해 보았습니다:

 curl -6 https://<host>/latest.json?order=created -v
*   Trying 2602:fd3f:3:ff01::2b:443...
* Immediate connect fail for 2602:fd3f:3:ff01::2b: Network is unreachable
* Closing connection 0
curl: (7) Couldn't connect to server

즉시 실패했습니다.

오늘 WSL에서 몇 가지 문제를 겪었기 때문에 새로 설치하도록 구성했습니다. 이는 asdf와 Node.js만 설치되고(모든 시스템 패키지가 업데이트된) 완전히 새로운 인스턴스입니다. 이것이 여전히 WSL 측의 잘못된 구성과 관련이 있는지 확실하지 않습니다. 현재는 모든 기본 설정을 사용해야 합니다.

언급된 그룹에 제 공용 IPv4 주소를 공유합니다.

음, 아직 개인 메시지를 보내는 방법을 모르겠네요. 아마도 신규 사용자라서 그런 것 같습니다.

더 흥미로운 점은… 공인 IPv4 주소를 확인하던 중, 해당 웹사이트가 IPv6 주소도 함께 표시하려고 시도했는데, 'IPv6가 감지되지 않았습니다’라고 나타났습니다. 그래서 아예 IPv6 연결이 있는지 확인해 봤더니, 이 페이지를 발견했습니다: TP-Link 무선 라우터에서 IPv6 서비스 설정 방법

해당 페이지에 따르면 라우터에는 IPv6를 사용하기 위한 전용 섹션이 있어야 합니다. 제 라우터에는 그런 섹션이 없고, 거기에 나와 있는 PPPoE v6 옵션도 보이지 않습니다. 따라서 제 라우터가 아예 IPv6를 지원하지 않는 것일 수 있으며, 이것이 IPv6 연결이 실패하는 이유(또는 Windows 설정에서 IPv4를 비활성화했을 때 인터넷 연결이 사실상 끊긴 이유)일 수 있습니다.

수정: 저는 이 라우터 TL-WR841N | 300Mbps Wireless N Router | TP-Link India를 사용 중인데, 제품 페이지에는 IPv6 지원이라고 되어 있습니다 :thinking:

수정2: 하지만 v6.testmyipv6.com 같은 사이트를 이용하면 ERR_NAME_NOT_RESOLVED 오류가 뜨므로, 제 경우 IPv6가 실제로 작동하지 않는 것으로 보입니다.

만약 이것이 문제라면, 왜 갑자기 이런 일이 발생하게 되었는지 여전히 알 수 없습니다. 꽤 오랫동안 잘 작동하고 있었거든요.

조금만 올려드렸습니다. :slight_smile: 이제 @team을 클릭하시면 메시지 버튼을 보실 수 있을 겁니다. :+1:

감사합니다, 보냈습니다!

추가 데이터 포인트로, IPv6를 사용하는 모바일 네트워크 핫스팟으로 전환했습니다. 이제 다음을 사용하여 확인 가능합니다:

curl -6 https://<host>/latest.json?order=created -v
*   Trying 2602:fd3f:3:ff01::2b:443...
* Immediate connect fail for 2602:fd3f:3:ff01::2b: Network is unreachable
* Closing connection 0
curl: (7) Couldn't connect to server

여전히 유사한 방식으로 실패합니다. 그러나 Windows 설정에서 IPv4를 비활성화하면 이전과 달리 인터넷에 접근할 수 있습니다. 따라서 새로운 데이터는 다음과 같습니다:

  • Windows에서 IPv4: 작동
  • Windows에서 IPv6: 작동
  • WSL에서 IPv4: 작동
  • WSL에서 IPv6: 실패

curl에 -4 또는 -6을 지정하지 않으면 IPv4로 폴백됩니다(IPv6은 즉시 실패):

curl https://<host>/latest.json?order=created -v
*   Trying 184.105.99.43:443...
*   Trying 2602:fd3f:3:ff01::2b:443...
* Immediate connect fail for 2602:fd3f:3:ff01::2b: Network is unreachable
* Connected to <host> (184.105.99.43) port 443 (#0)

또한 다음을 실행했습니다:

ping -6 google.com

Windows와 WSL 모두에서 실행했습니다. Windows에서는 작동하지만 WSL에서는 실패합니다. 이제 제 WSL 인스턴스의 IPv6 연결성에 문제가 있다는 것을 충분히 확인하고 검증한 것 같습니다. 문제인 것 같습니다: ipv6 only address 도달 불가 · Issue #4518 · microsoft/WSL (github.com)

드디어 성공했습니다!

위에서 링크된 토론을 바탕으로 다음 내용을

[experimental]
networkingMode=mirrored

.wslconfig 파일에 추가했고, 덕분에 WSL 인스턴스 내에서 IPv6 연결이 가능해졌습니다. 이제 다음 명령어를 성공적으로 실행할 수 있습니다:

curl -6 https://<host>/latest.json?order=created

다만, Wi-Fi에서는 여전히 작동하지 않습니다. 아마도 이 오래된 라우터가 IPv6을 지원하지 않기 때문일 것입니다. 휴대폰 핫스팟을 사용하면 정상적으로 작동합니다.

@JammyDodger 님, 좋은 조언을 주셔서 정말 감사합니다. 올바른 방향으로 이끌어주셨습니다!

좋아요, 원본 게시글과 이 글 사이에 분명히 무언가 변경되었네요. 처음에는 다음과 같은 오류가 발생했었거든요:

이제 작동하는 것을 보니 다행입니다.

그래도 여전히 의아하네요. 전에 언급했듯이, WSL을 통해 curl 요청을 보내는 것은 이전에도 가능했고, 그건 잘 동작했어요. 그런데 어째서인지 제 애플리케이션에서 Axios를 사용할 때는 실패하더라고요 (아마 Axios나 Node.js 쪽 문제일까요?).

어쨌든 curl -4는 여전히 잘 동작하고, 원래 게시글에 보여드린 것처럼 httpFamily를 지정하면 Axios도 잘 동작합니다. 하지만 이건 더 이상 잠을 설칠 정도로 신경 쓸 일은 아니에요.

확인을 위해 Node.js 18으로 되돌려 보았더니 httpFamily를 지정하지 않아도 연결할 수 있었습니다. 따라서 이는 분명 Node.js 19 또는 20에서 변경된 부분입니다. 덕분에 IPv6 관련 사항을 알게 되었지만, 그렇지 않았다면 이 문제를 검색하거나 배웠을지 모르겠습니다.

또한 Node.js 대신 bun으로 코드를 실행할 때 IPv4 관련 설정을 지정하지 않아도 정상적으로 동작합니다. Node.js 측에 문의를 남겼습니다: https://github.com/orgs/nodejs/discussions/50826