ping6.net
모범 사례

웹 개발자를 위한 IPv6

웹 개발자가 IPv6에 대해 알아야 할 사항: 대괄호가 있는 URL, 데이터베이스 저장소, 소켓 프로그래밍 및 피해야 할 일반적인 버그입니다.

ping6.net2024년 12월 14일5 min read
IPv6웹 개발프로그래밍데이터베이스소켓

웹 개발자를 위한 IPv6#

"나중에 문제가 될 것"이기 때문에 IPv6를 무시해 왔다면 나중이 지금입니다. 모바일 네트워크는 IPv6 우선으로 실행됩니다. NAT64 뒤의 일부 사용자는 IPv6를 통해서만 연결할 수 있습니다. 개발 시스템의 localhost조차도 127.0.0.1 전에 ::1로 확인될 가능성이 높습니다.

이 가이드는 실제로 알아야 할 사항을 다룹니다. URL에서 IPv6 처리, 데이터베이스에 주소 저장, 소켓 프로그래밍 및 주의하지 않으면 물릴 버그입니다.

TL;DR - 빠른 요약

핵심 포인트:

  • URL의 IPv6 주소에는 대괄호 필요: http://[2001:db8::1]:8080
  • PostgreSQL의 경우 INET 유형 사용, MySQL의 경우 VARCHAR(45) 또는 VARBINARY(16) 사용
  • 듀얼 스택 지원을 위해 ::에 바인딩, 클라이언트 연결을 위해 getaddrinfo() 사용
  • IPv6 정규식을 피하고 적절한 파싱 라이브러리 사용
  • 개인 정보 보호 확장으로 인해 IPv6 주소가 변경되므로 정확한 주소로 속도 제한하지 말 것

바로가기: URL의 IPv6 | 데이터베이스 저장소 | 소켓 프로그래밍 | 일반적인 버그


URL의 IPv6#

IPv6 주소에는 URL의 포트 구분 기호와 충돌하는 콜론이 포함되어 있습니다. 해결책은 대괄호 표기법입니다:

http://[2001:db8::1]:8080/api/users
https://[2606:2800:220:1:248:1893:25c8:1946]/

대괄호가 없으면 파서가 주소가 끝나는 위치와 포트가 시작하는 위치를 알 수 없습니다. 이것은 다음에 적용됩니다:

  • HTTP/HTTPS URL
  • WebSocket 연결(ws://[::1]:3000)
  • 데이터베이스 연결 문자열
  • 모든 URI 체계

프로그래밍 방식으로 URL을 생성할 때 IPv6 주소를 대괄호로 묶습니다. 파싱할 때 유효성 검사 또는 저장 전에 제거합니다.


데이터베이스 저장소#

전형적인 실수는 IP 주소에 VARCHAR(15)를 사용하는 것입니다. 이것은 IPv4에 적합합니다(최대 15자: 255.255.255.255) 하지만 IPv6에는 적합하지 않습니다.

PostgreSQL에는 IPv4와 IPv6를 모두 처리하는 기본 INET 유형이 있습니다:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  ip_address INET NOT NULL
);
 
-- 자동 유효성 검사 및 정규화
INSERT INTO users (ip_address) VALUES ('2001:db8::1');
INSERT INTO users (ip_address) VALUES ('192.0.2.1');

INET 유형은 주소를 효율적으로 저장하고, 삽입 시 유효성을 검사하며, <<>> 연산자를 사용하여 서브넷 일치와 같은 네트워크 작업을 지원합니다.

MySQL에는 기본 유형이 없으므로 다음 중 하나를 사용합니다:

  • VARBINARY(16) - 이진 표현 저장(IPv6의 경우 16바이트, IPv4의 경우 4바이트)
  • VARCHAR(45) - 문자열 표현 저장(확장된 IPv6의 최대 길이)

이진 저장이 더 효율적이지만 변환 함수가 필요합니다:

-- 저장
INSERT INTO users (ip_address) VALUES (INET6_ATON('2001:db8::1'));
 
-- 검색
SELECT INET6_NTOA(ip_address) FROM users;

정규화가 중요합니다. IPv6 주소에는 여러 유효한 표현이 있습니다:

  • 2001:0db8:0000:0000:0000:0000:0000:0001
  • 2001:db8::1(압축)
  • 2001:db8:0:0:0:0:0:1(부분적으로 압축)

비교 또는 인덱싱 전에 항상 정규화합니다. 대부분의 라이브러리에는 정식 형식 함수가 있습니다.


소켓 프로그래밍#

소켓 API는 IPv4와 IPv6를 별도의 주소 패밀리로 취급합니다. 최신 애플리케이션은 둘 다 지원해야 합니다.

Node.js는 기본적으로 듀얼 스택 소켓입니다:

const http = require('http');
 
// ::(모든 IPv6 주소)에 바인딩하고 매핑을 통해 IPv4 수락
const server = http.createServer((req, res) => {
  res.end(`Your IP: ${req.socket.remoteAddress}`);
});
 
server.listen(3000, '::');

:: 주소는 0.0.0.0의 IPv6 동등물로, 모든 인터페이스에서 연결을 수락합니다. 대부분의 시스템은 IPv4 매핑 IPv6 주소(::ffff:192.0.2.1)를 지원하여 단일 IPv6 소켓이 두 프로토콜을 모두 처리할 수 있도록 합니다.

Python은 명시적인 듀얼 스택 처리가 필요합니다:

import socket
 
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
sock.bind(('::', 8080))
sock.listen(5)

IPV6_V6ONLY 플래그는 소켓이 IPv6만 수락할지 아니면 IPv4와 IPv6 둘 다 수락할지 제어합니다.

Go는 듀얼 스택을 기본값으로 만듭니다:

listener, err := net.Listen("tcp", ":8080")
// IPv4 및 IPv6 둘 다 자동으로 수신 대기

클라이언트 연결의 경우 항상 수동으로 주소를 해결하는 대신 getaddrinfo()(또는 언어의 동등물)를 사용합니다. IPv4, IPv6 및 듀얼 스택 시나리오를 올바르게 처리합니다.


로컬 테스트#

대부분의 시스템은 localhost::1(IPv6)과 127.0.0.1(IPv4) 둘 다로 확인합니다. /etc/hosts를 확인하세요:

127.0.0.1       localhost
::1             localhost

애플리케이션이 127.0.0.1에만 바인딩하면 IPv6 연결을 수락하지 않습니다. 듀얼 스택을 위해 ::에 바인딩하거나 두 주소 모두에 명시적으로 바인딩합니다.

IPv6 전용 동작을 테스트하려면 루프백 인터페이스에서 IPv4를 비활성화하거나 HTTP 클라이언트에서 [::1]을 명시적으로 사용합니다:

curl http://[::1]:3000/

현실적인 테스트를 위해 IPv6 전용 테스트 네트워크를 사용합니다. 많은 클라우드 제공업체가 NAT64/DNS64와 같은 에지 케이스를 강제로 처리하도록 하는 IPv6 전용 인스턴스를 제공합니다.


CDN 및 오리진 서버#

대부분의 CDN(Cloudflare, Fastly, AWS CloudFront)은 기본적으로 IPv6를 지원합니다. IPv6를 통해 클라이언트에 콘텐츠를 제공하고 필요한 경우 오리진에 대해 IPv4로 변환합니다.

문제: API를 직접 노출하는 경우(CDN 우회) 오리진 서버는 IPv6를 지원해야 합니다. DNS에 AAAA 레코드가 있고 방화벽이 IPv6 트래픽을 허용하는지 확인합니다.


일반적인 버그#

정규식 유효성 검사. IPv6 정규식 패턴은 악명 높게 복잡하고 일반적으로 잘못되었습니다:

// 하지 마세요
const ipv6Regex = /^([0-9a-f]{1,4}:){7}[0-9a-f]{1,4}$/i;
// :: 압축, IPv4 매핑 주소, Zone ID 등에서 실패...

대신 적절한 파싱 라이브러리를 사용합니다. 모든 주요 언어에는 IPv6를 올바르게 처리하는 것이 있습니다.

하드코딩된 IPv4 주소. 다음과 같은 패턴을 코드베이스에서 검색합니다:

const API_SERVER = 'http://192.168.1.100:3000';  // IPv6와 작동하지 않음

대신 호스트 이름을 사용하거나 두 주소 패밀리를 모두 지원합니다.

IP 기반 속도 제한. 개인 정보 보호 확장으로 인해 IPv6 주소가 자주 변경됩니다. 정확한 주소로 속도 제한하면 실패합니다. 대신 /64 서브넷으로 제한합니다:

// 나쁨
const key = `rate:${ipAddress}`;
 
// IPv6에 더 좋음
const key = `rate:${ipv6ToSubnet64(ipAddress)}`;

IPv6 지원이 없는 라이브러리. 오래된 HTTP 클라이언트, 데이터베이스 드라이버 및 네트워킹 라이브러리는 IPv6를 처리하지 못할 수 있습니다. localhost만이 아니라 실제 IPv6 주소로 테스트합니다. 라이브러리가 실패하면 업데이트 또는 대안을 확인합니다.


체크리스트#

애플리케이션이 다음과 같은 경우 IPv6 준비가 완료되었습니다:

  • 데이터베이스 열이 45자 문자열을 저장하거나 기본 IP 유형을 사용할 수 있음
  • 서버가 ::에 바인딩하거나 IPv4 및 IPv6 둘 다에서 명시적으로 수신 대기
  • 클라이언트 코드가 이름 확인에 getaddrinfo() 또는 동등물 사용
  • IP 유효성 검사가 정규식에 의존하지 않음
  • 구성에 하드코딩된 IPv4 주소가 없음
  • 속도 제한 및 지리적 위치가 IPv6 서브넷 처리
  • URL 파싱이 대괄호 표기법 처리

IPv6는 더 이상 선택 사항이 아닙니다. 처음부터 빌드하면 나중에 고통스러운 마이그레이션을 피할 수 있습니다.


관련 기사#

직접 해보세요

IPv6 검증기Ping 도구를 사용하여 애플리케이션의 IPv6 지원을 테스트하세요.