참고
아래와 같은 IP 변환기를 만들어보자.
위의 결과는 링크에서 확인할 수 있다.
JavaScript IP 변환
링크를 참고하여 IP 관련 함수를 JavaScript로 구현해보자.
- IPv4 Invalid Check
- IPv6 Invalid Check
- IPv6 Invalid Check 축약형
- Integer to IPv4
- IPv4 to Integer
- Integer to IPv6
- IPv6 to Integer
- IPv6 Long to Short
- IPv6 Short to Long
IPv4 Invalid Check
정규 표현식을 이용하여 패턴을 확인한 후, 각 숫자가 255 이하인지 체크하는 코드도 추가하였다.
const isValidIPv4 = (ipv4) => {
const pattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
const parts = ipv4.match(pattern);
if (!parts) {
return false;
}
for (let i = 1; i <= 4; i++) {
const octet = parseInt(parts[i], 10);
if (octet < 0 || octet > 255 || (octet === 0 && parts[i].length > 1)) {
return false;
}
}
return true;
};
IPv6 Invalid Check
정규 표현식을 이용하여 A-F, a-f, 0-9 값만 가지는 것을 확인한다.
const isValidIPv6 = (ipv6) => {
const pattern= /^(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}$/;
return pattern.test(ipv6);
}
IPv6 Invalid Check 축약형
축약형의 경우 ::이 포함되는 경우도 체크한다.
const isValidIPv6Short = (ipv6) => {
const pattern = /^(([A-Fa-f0-9]{1,4}:){1,7}|:)((:[A-Fa-f0-9]{1,4}){1,7}|:)$/;
return pattern.test(ipv6);
}
Integer to IPv4
255.255.255.255는 정수로 변환할 때, 4294967295(232 - 1)가 된다.
따라서 0보다 작거나 이 값보다 큰 값은 IPv4로 변환할 수 없다.
const integerToIpv4 = (value) => {
let maxValue = 4294967295;
let integer = Number(value);
if (isNaN(integer) === true || integer < 0 || integer > maxValue) {
return ERROR_CODE;
}
return (
((integer >> 24) & 0xff) + "." +
((integer >> 16) & 0xff) + "." +
((integer >> 8) & 0xff) + "." +
(integer & 0xff)
);
};
IPv4 to Integer
IPv4가 유효한 값인 경우라면, split한 후 각 숫자를 처리한다.
const ipv4ToInteger = (ipv4) => {
if(isValidIPv4(ipv4) === false) return ERROR_CODE;
const octets = ipv4.split('.').map(Number);
return (octets[0] * 256 ** 3) + (octets[1] * 256 ** 2) + (octets[2] * 256 ** 1) + (octets[3] * 256 ** 0);
};
Integer to IPv6
IPv6를 정수로 변환할 때 가장 큰 수는 340282366920938463463374607431768211455다.
이 값을 이용하려면 자바스크립트에서 BigInt를 이용하면 된다.
리액트에서는 global BigInt 주석을 추가해야 사용할 수 있따.
/* global BigInt */
const integerToIpv6 = (value) => {
let integer;
try {
integer = BigInt(value);
} catch (e) {
return ERROR_CODE;
}
let maxValue = BigInt(340282366920938463463374607431768211455);
if (integer < 0 || integer > maxValue) return ERROR_CODE;
let hexString = integer.toString(16);
while (hexString.length < 32) {
hexString = "0" + hexString;
}
const parts = [];
for (let i = 0; i < 8; i++) {
parts.push(hexString.slice(i * 4, (i + 1) * 4));
}
return parts.join(":");
};
IPv6 to Integer
마찬가지로 값이 크기 때문에 BigInt를 이용해서 처리한다.
BigInt("0x" + [0~F])로 처리하면 16진법인 것을 알고 10진법으로 변환해준다.
const ipv6ToInteger = (ipv6) => {
if(isValidIPv6(ipv6) === false) return ERROR_CODE;
const parts = ipv6.split(':');
let result = BigInt(0);
for (let i = 0; i < parts.length; i++) {
result = (result << BigInt(16)) + BigInt("0x" + parts[i]);
}
return String(result);
};
IPv6 Long to Short
0000에 해당하는 부분을 제거하였다.
const ipv6LongToShort = (ipv6) => {
if(isValidIPv6(ipv6) === false) return ERROR_CODE;
const blocks = ipv6.split(":");
for (let i = 0; i < blocks.length; i++) {
if (blocks[i] === "0000") {
blocks[i] = "";
}
}
return blocks.join(":").replace(/:{3,}/g, "::");
};
IPv6 Short to Long
Short에서 0을 제거했다면, Long에서는 0을 채우면 된다.
const ipv6ShortToLong = (ipv6) => {
if(isValidIPv6Short(ipv6) === false) return ERROR_CODE;
let blocks = ipv6.split(':');
if (blocks.length === 8) {
return ipv6;
}
let index = blocks.indexOf('');
blocks.splice(index, 1, ...Array(8 - blocks.length + 1).fill('0'));
let expandedIPv6 = blocks.map(block => {
while (block.length < 4) {
block = '0' + block;
}
return block;
}).join(':');
return expandedIPv6;
}
리액트 IP Converter 구현
위의 메서드를 이용하여 리액트에서 구현하면 아래와 같다.
/* global BigInt */
import React, { useState } from "react";
import Box from "@mui/material/Box";
import TextField from "@mui/material/TextField";
const ERROR_CODE = "Error";
const isValidIPv4 = (ipv4) => {
const pattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
const parts = ipv4.match(pattern);
if (!parts) {
return false;
}
for (let i = 1; i <= 4; i++) {
const octet = parseInt(parts[i], 10);
if (octet < 0 || octet > 255 || (octet === 0 && parts[i].length > 1)) {
return false;
}
}
return true;
};
const isValidIPv6 = (ipv6) => {
const pattern= /^(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}$/;
return pattern.test(ipv6);
}
const isValidIPv6Short = (ipv6) => {
const pattern = /^(([A-Fa-f0-9]{1,4}:){1,7}|:)((:[A-Fa-f0-9]{1,4}){1,7}|:)$/;
return pattern.test(ipv6);
}
const integerToIpv4 = (value) => {
let maxValue = 4294967295;
let integer = Number(value);
if (isNaN(integer) === true || integer < 0 || integer > maxValue) {
return ERROR_CODE;
}
return (
((integer >> 24) & 0xff) + "." +
((integer >> 16) & 0xff) + "." +
((integer >> 8) & 0xff) + "." +
(integer & 0xff)
);
};
const ipv4ToInteger = (ipv4) => {
if(isValidIPv4(ipv4) === false) return ERROR_CODE;
const octets = ipv4.split('.').map(Number);
return (octets[0] * 256 ** 3) + (octets[1] * 256 ** 2) + (octets[2] * 256 ** 1) + (octets[3] * 256 ** 0);
};
const integerToIpv6 = (value) => {
let integer;
try {
integer = BigInt(value);
} catch (e) {
return ERROR_CODE;
}
let maxValue = BigInt(340282366920938463463374607431768211455);
if (integer < 0 || integer > maxValue) return ERROR_CODE;
let hexString = integer.toString(16);
while (hexString.length < 32) {
hexString = "0" + hexString;
}
const parts = [];
for (let i = 0; i < 8; i++) {
parts.push(hexString.slice(i * 4, (i + 1) * 4));
}
return parts.join(":");
};
const ipv6ToInteger = (ipv6) => {
if(isValidIPv6(ipv6) === false) return ERROR_CODE;
const parts = ipv6.split(':');
let result = BigInt(0);
for (let i = 0; i < parts.length; i++) {
result = (result << BigInt(16)) + BigInt("0x" + parts[i]);
}
return String(result);
};
const ipv6LongToShort = (ipv6) => {
if(isValidIPv6(ipv6) === false) return ERROR_CODE;
const blocks = ipv6.split(":");
for (let i = 0; i < blocks.length; i++) {
if (blocks[i] === "0000") {
blocks[i] = "";
}
}
return blocks.join(":").replace(/:{3,}/g, "::");
};
const ipv6ShortToLong = (ipv6) => {
if(isValidIPv6Short(ipv6) === false) return ERROR_CODE;
let blocks = ipv6.split(':');
if (blocks.length === 8) {
return ipv6;
}
let index = blocks.indexOf('');
blocks.splice(index, 1, ...Array(8 - blocks.length + 1).fill('0'));
let expandedIPv6 = blocks.map(block => {
while (block.length < 4) {
block = '0' + block;
}
return block;
}).join(':');
return expandedIPv6;
}
const deleteFrontZero = (str) => {
if (str === "0") return str;
return str.replace(/^0+/, "");
};
const IPConverter = () => {
const [info, setInfo] = useState({
integer: "0",
ipv4: "0.0.0.0",
ipv6_short: "::",
ipv6_long: "0000:0000:0000:0000:0000:0000:0000:0000",
});
const changeInteger = (value) => {
let ipv6 = integerToIpv6(value);
setInfo({
integer: deleteFrontZero(String(value)),
ipv4: integerToIpv4(value),
ipv6_short: ipv6LongToShort(ipv6),
ipv6_long: ipv6,
});
};
const changeIpv4 = (value) => {
let integer = ipv4ToInteger(value);
let ipv6 = integerToIpv6(integer);
setInfo({
integer: integer,
ipv4: value,
ipv6_short: ipv6LongToShort(ipv6),
ipv6_long: ipv6,
});
};
const changeIpv6Short = (value) => {
let ipv6Long = ipv6ShortToLong(value);
let bigInteger = ipv6ToInteger(ipv6Long);
setInfo({
integer: bigInteger,
ipv4: integerToIpv4(bigInteger),
ipv6_short: value,
ipv6_long: ipv6Long,
});
}
const changeIpv6Long = (value) => {
let bigInteger = ipv6ToInteger(value);
setInfo({
integer: bigInteger,
ipv4: integerToIpv4(bigInteger),
ipv6_short: ipv6LongToShort(value),
ipv6_long: value,
});
}
return (
<div>
<Box
component="form"
sx={{ m: 5, display: "flex", flexDirection: "column", width: "800px" }}
noValidate
autoComplete="off"
>
<TextField
sx={{ marginBottom: 3 }}
color={info.integer === ERROR_CODE ? "error" : "primary"}
focused
error={false}
label="Integer"
value={info.integer}
onChange={(e) => changeInteger(e.target.value)}
/>
<TextField
sx={{ marginBottom: 3 }}
color={info.ipv4 === ERROR_CODE ? "error" : "primary"}
focused
error={false}
label="IPv4"
value={info.ipv4}
onChange={(e) => changeIpv4(e.target.value)}
/>
<TextField
sx={{ marginBottom: 3 }}
color={info.ipv6_short === ERROR_CODE ? "error" : "success"}
focused
error={false}
label="IPv6 (short)"
value={info.ipv6_short}
onChange={(e) => changeIpv6Short(e.target.value)}
/>
<TextField
sx={{ marginBottom: 3 }}
color={info.ipv6_long === ERROR_CODE ? "error" : "success"}
focused
error={false}
label="Ipv6 (long)"
value={info.ipv6_long}
onChange={(e) => changeIpv6Long(e.target.value)}
/>
</Box>
</div>
);
};
export default IPConverter;
'개발 > React' 카테고리의 다른 글
리액트 - useLocation으로 페이지 이동시 스크롤 상단으로 초기화하기 (Scroll to Top on Page Transition) (0) | 2023.12.16 |
---|---|
리액트 - Toast UI Editor로 메일 보내기 with nodemailer (0) | 2023.12.10 |
React Material - Mui Pagination으로 댓글 페이지로 나누기 (0) | 2023.11.19 |
리액트 - 쿠키로 GitHub OAuth 로그인 인증 관리하기 with react-cookie (0) | 2023.11.15 |
리액트 - GitHub RESTful API로 댓글 저장하기 with react-comments-section (0) | 2023.11.15 |
댓글