1. 문자 → 아스키코드
charCodeAt(index): 특정 위치의 문자의 아스키코드를 반환합니다.
let char = "A";
let asciiCode = char.charCodeAt(0); // "A"의 아스키코드
console.log(asciiCode); // 65
2. 문자열 → 아스키코드 배열
문자열의 각 문자를 아스키코드로 변환하여 배열로 저장합니다.
방법: split 또는 전개 연산자 [...string]을 사용한 후 map으로 변환.
let str = "Hello";
let asciiArray = [...str].map((char) => char.charCodeAt(0));
console.log(asciiArray); // [72, 101, 108, 108, 111]
3. 아스키코드 → 문자
String.fromCharCode(...codes): 숫자(아스키코드)를 문자로 변환합니다.
let asciiCode = 72;
let char = String.fromCharCode(asciiCode);
console.log(char); // "H"
4. 아스키코드 배열 → 문자열
아스키코드 배열을 하나의 문자열로 변환합니다.
방법: 배열에 String.fromCharCode를 사용하여 문자 배열로 변환 후 결합.
let asciiArray = [72, 101, 108, 108, 111];
let str = asciiArray.map((code) => String.fromCharCode(code)).join("");
console.log(str); // "Hello"
전체 예시
아래는 위의 모든 단계를 하나의 함수로 구현한 예제입니다.
function asciiExample(inputString) {
// 1. 문자 → 아스키코드
let singleChar = inputString[0];
let asciiCode = singleChar.charCodeAt(0);
// 2. 문자열 → 아스키코드 배열
let asciiArray = [...inputString].map((char) => char.charCodeAt(0));
// 3. 아스키코드 → 문자
let charFromAscii = String.fromCharCode(asciiCode);
// 4. 아스키코드 배열 → 문자열
let stringFromAsciiArray = asciiArray.map((code) => String.fromCharCode(code)).join("");
return {
asciiCode,
asciiArray,
charFromAscii,
stringFromAsciiArray,
};
}
// 테스트 실행
let result = asciiExample("Hello");
console.log(result);
/*
{
asciiCode: 72, // 첫 번째 문자의 아스키코드
asciiArray: [72, 101, 108, 108, 111], // 문자열의 아스키코드 배열
charFromAscii: "H", // 아스키코드로부터 문자
stringFromAsciiArray: "Hello" // 아스키코드 배열로부터 문자열
}
*/
추가적인 설명
- 아스키코드의 범위:
- 소문자: 97~122 (예: a=97, z=122)
- 대문자: 65~90 (예: A=65, Z=90)
- 숫자: 48~57 (예: 0=48, 9=57)
- 활용 사례:
- 암호화: 아스키코드 변환 후 특정 규칙으로 수정.
- 문자열 비교: 알파벳 순서를 판단하거나 대소문자 변환.
아스키코드를 기반으로 특정 문자열을 암호화하는 알고리즘을 설계 방식
문자열 암호화를 위해 아스키코드 기반 알고리즘을 설계할 때 다음 방식을 사용할 수 있습니다:
- 시저 암호 (문자 이동)
- 각 문자의 아스키코드 값을 일정량(n) 이동시키는 방식.
- 대소문자를 구분하고, 범위를 벗어날 경우 순환 처리.
function caesarCipher(str, n) { return [...str] .map((char) => { let charCode = char.charCodeAt(0); // 소문자 처리 if (charCode >= 97 && charCode <= 122) { return String.fromCharCode(((charCode - 97 + n) % 26) + 97); } // 대문자 처리 if (charCode >= 65 && charCode <= 90) { return String.fromCharCode(((charCode - 65 + n) % 26) + 65); } // 특수 문자나 공백은 그대로 반환 return char; }) .join(""); } console.log(caesarCipher("Hello World!", 3)); // "Khoor Zruog!"
- XOR 암호화
- 각 문자의 아스키코드와 비밀 키 값을 XOR 연산으로 암호화. 복호화 시에도 같은 키를 사용.
- 암호화 강도는 키값의 복잡성에 따라 달라집니다.
function xorCipher(str, key) { return [...str] .map((char) => String.fromCharCode(char.charCodeAt(0) ^ key)) .join(""); } let encrypted = xorCipher("Hello", 42); let decrypted = xorCipher(encrypted, 42); console.log(encrypted); // 암호화된 문자열 console.log(decrypted); // "Hello"
- 난수 기반 암호화
- 문자의 아스키코드를 무작위 값으로 변환 후 키값과 함께 저장.
- 복호화를 위해 반드시 키값이 필요.
function randomEncrypt(str) { let key = [...str].map(() => Math.floor(Math.random() * 100)); let encrypted = [...str].map((char, i) => char.charCodeAt(0) + key[i]); return { encrypted, key }; } function randomDecrypt(encrypted, key) { return encrypted.map((code, i) => String.fromCharCode(code - key[i])).join(""); } let { encrypted, key } = randomEncrypt("Hello"); let decrypted = randomDecrypt(encrypted, key); console.log(encrypted, key); // 암호화된 코드와 키 console.log(decrypted); // "Hello"
단순 암호화에는 시저 암호와 XOR이 적합하며, 난수 기반은 더 높은 보안성을 제공합니다.
유니코드 문자(예: 한글, 이모지)의 처리
유니코드 문자를 처리하려면 다음을 고려해야 합니다:
- 아스키코드와 유니코드의 차이
- 아스키코드는 0~127 범위(1바이트)지만, 유니코드는 훨씬 큰 범위를 포함합니다.
- 예: 한글은 0xAC00~0xD7A3, 이모지는 2바이트 이상.
- 유니코드의 가변 길이
- 이모지나 복합 문자는 여러 코드포인트로 구성될 수 있으므로, charCodeAt 대신 codePointAt 사용이 필요.
let emoji = "😊"; console.log(emoji.codePointAt(0)); // 128522
- 복합 문자 처리
- Normalization이 필요할 수 있음(예: 한글 조합형 가는 단일 문자와 조합 문자가 있음).
- 암호화/복호화에서 올바른 순환
- 유니코드의 범위는 불규칙적이므로, 특정 범위를 설정하거나 직접 매핑 테이블을 만들어야 합니다.
예제: 한글 처리
function encryptKorean(str, n) {
return [...str]
.map((char) => {
let code = char.charCodeAt(0);
// 한글 유니코드 범위 처리
if (code >= 0xAC00 && code <= 0xD7A3) {
return String.fromCharCode(((code - 0xAC00 + n) % 11172) + 0xAC00);
}
return char; // 한글이 아니면 그대로 반환
})
.join("");
}
console.log(encryptKorean("안녕하세요", 3)); // "앋녕핟아옇"
유니코드 처리를 위해선 codePointAt과 유니코드 범위에 대한 이해가 필요합니다.
한글과 이모지를 동시에 암호화하면서 범위 충돌을 방지하는 방법
한글과 이모지를 동시에 암호화하려면, 두 가지 과제를 해결해야 합니다:
- 범위 구분
- 한글(0xAC00~0xD7A3)과 이모지(다양한 유니코드 블록)에 대해 각각의 범위를 구분하고 처리해야 합니다.
- 범위가 겹치지 않도록 특정 기준을 설정하여 고유한 변환 로직을 적용합니다.
- 암호화 일관성 유지
- 서로 다른 범위의 문자를 암호화해도 일관된 복호화가 가능하도록 설계합니다.
설계 아이디어
- 범위 기반 분리
- 입력된 문자의 유니코드 값을 확인하여 한글, 이모지, 기타 문자로 분리합니다.
- 각 범위에 대해 다른 암호화 로직을 적용합니다.
- 범위 내 암호화
- 범위를 벗어나지 않도록 % 연산으로 순환 처리.
- 예: 한글은 11172(한글 글자 수), 이모지는 특정 범위로 순환.
- 범위 태깅
- 암호화된 값을 복호화할 때 어떤 범위에서 변환된 것인지 알 수 있도록 태그를 추가합니다.
예제 코드
function encryptText(input, n) {
return [...input]
.map((char) => {
let code = char.codePointAt(0);
// 한글 처리
if (code >= 0xAC00 && code <= 0xD7A3) {
// 한글은 0xAC00을 기준으로 순환
return String.fromCharCode(((code - 0xAC00 + n) % 11172) + 0xAC00);
}
// 이모지 처리 (대표적 범위만 예로 설정)
if ((code >= 0x1F600 && code <= 0x1F64F) || // Smileys
(code >= 0x1F300 && code <= 0x1F5FF)) { // Miscellaneous
return String.fromCodePoint(((code - 0x1F600 + n) % 80) + 0x1F600);
}
// 기타 문자 처리
return char; // 변환하지 않고 그대로 반환
})
.join("");
}
function decryptText(input, n) {
return [...input]
.map((char) => {
let code = char.codePointAt(0);
// 한글 복호화
if (code >= 0xAC00 && code <= 0xD7A3) {
return String.fromCharCode(((code - 0xAC00 - n + 11172) % 11172) + 0xAC00);
}
// 이모지 복호화
if ((code >= 0x1F600 && code <= 0x1F64F) ||
(code >= 0x1F300 && code <= 0x1F5FF)) {
return String.fromCodePoint(((code - 0x1F600 - n + 80) % 80) + 0x1F600);
}
// 기타 문자 복호화
return char;
})
.join("");
}
// 테스트
let originalText = "안녕하세요 😊🌍";
let encrypted = encryptText(originalText, 3);
let decrypted = decryptText(encrypted, 3);
console.log("Original:", originalText); // 안녕하세요 😊🌍
console.log("Encrypted:", encrypted); // 암호화된 결과
console.log("Decrypted:", decrypted); // 안녕하세요 😊🌍
코드 동작 설명
- 한글 처리
- 한글 유니코드 범위(0xAC00~0xD7A3) 내에서만 순환 처리.
- 글자 수는 11172자이며, char - 0xAC00로 0부터 시작해 이동 후 복원.
- 이모지 처리
- 대표적인 이모지 유니코드 범위(예: 0x1F600~0x1F64F)를 대상으로 순환 처리.
- char - 시작점으로 0부터 시작하여 범위를 넘지 않게 % 연산.
- 기타 문자 처리
- 한글과 이모지 범위 외의 문자(영어, 숫자 등)는 그대로 반환.
- 복호화
- 암호화 시와 동일한 로직에서 + n 대신 - n을 적용.
- % 연산으로 음수 값을 처리하여 올바른 복원.
주의 사항
- 이모지 범위
- 이모지는 다양한 유니코드 블록에 걸쳐 있어 정확한 범위를 지정해야 함.
- 범위를 명확히 설정하지 않으면 예상치 못한 문자가 처리될 수 있음.
- 성능
- codePointAt과 fromCodePoint는 다중 바이트 문자를 처리하지만, 배열 변환으로 성능이 낮아질 수 있음.
- 복합 문자
- 조합형 한글이나 이모지의 복합 형태(예: 🤷♂️)는 추가 처리가 필요할 수 있음.
찾아보기
한글 외 다른 언어(예: 일본어, 중국어)에도 동일한 방식으로 암호화를 적용하려면 어떤 점을 고려해야 할까요?
이모지나 특수 문자의 범위를 더 안전하게 처리하려면 어떤 방법으로 유니코드 블록을 확장할 수 있을까요?
아스키코드 배열에서 범위를 벗어나는 숫자를 처리하는 방법
아스키코드 배열을 처리할 때, 범위를 벗어나는 숫자(예: 256 이상의 값)는 다음과 같은 방법으로 해결할 수 있습니다:
- 순환 처리 (% 연산)
- 값이 일정 범위를 초과하면 나머지를 사용해 순환적으로 처리합니다.
let code = 200; let wrapped = code % 128; // 0~127 범위로 순환 console.log(wrapped); // 72
- 범위 클램핑 (Clamping)
- 값을 특정 최소/최대 범위로 제한합니다.
function clamp(value, min, max) { return Math.max(min, Math.min(value, max)); } let clamped = clamp(150, 0, 127); // 최대값을 127로 제한 console.log(clamped); // 127
- 오류 처리
- 범위를 벗어나는 값을 발견했을 때 예외를 발생시키거나 기본값으로 설정.
function handleOutOfRange(code) { if (code < 0 || code > 127) throw new Error("Invalid ASCII Code"); return code; } try { console.log(handleOutOfRange(150)); // 오류 발생 } catch (error) { console.error(error.message); }
- 범위 기반 커스텀 로직
- 문자 범위에 따라 다른 방식으로 처리.
let code = 150; if (code > 122) code -= 26; // 소문자 기준 순환 console.log(String.fromCharCode(code)); // 변환된 문자
범위를 벗어나는 값은 순환(%), 클램핑, 또는 커스텀 로직으로 처리 가능합니다.
시저함수
https://ashen99.tistory.com/484
대 소문자 유지
1. 소문자 처리 코드
let charCode = "a".charCodeAt(0); // 97
let n = 2;
let result = String.fromCharCode(((charCode - 97 + n) % 26) + 97);
console.log(result); // "c"
if (charCode >= 97 && charCode <= 122) {
return String.fromCharCode(((charCode - 97 + n) % 26) + 97);
}
- 조건 확인 (charCode >= 97 && charCode <= 122)
- charCode가 소문자 알파벳의 아스키코드 범위(97~122)에 있는지 확인.
- 예: a=97, z=122.
- 문자를 0~25 범위로 변환 (charCode - 97)
- 소문자의 아스키코드를 97을 기준으로 0부터 시작하는 숫자로 변환.
- 예: a=0, b=1, ..., z=25.
- 시프트(+ n)
- 변환된 숫자에 n(주어진 이동 값)을 더해 새로운 위치를 계산.
- 예: a(0)에서 n=2를 더하면 2가 되어 c로 이동.
- 순환 처리 (% 26)
- 26으로 나눈 나머지를 사용해 범위를 벗어나지 않도록 순환.
- 예: z(25)에서 n=2를 더하면 27이 되는데, 27 % 26 = 1로 순환하여 b로 변환.
- 아스키코드로 복원 (+ 97)
- 다시 원래 소문자 범위로 복원하기 위해 97을 더함.
- 예: 0이 a가 되고, 25가 z가 됨.
- 문자 반환 (String.fromCharCode)
- 최종 계산된 아스키코드를 문자로 변환해 반환.
2. 대문자 처리 코드
let charCode = "Z".charCodeAt(0); // 90
let n = 2;
let result = String.fromCharCode(((charCode - 65 + n) % 26) + 65);
console.log(result); // "B"
if (charCode >= 65 && charCode <= 90) {
return String.fromCharCode(((charCode - 65 + n) % 26) + 65);
}
- 조건 확인 (charCode >= 65 && charCode <= 90)
- charCode가 대문자 알파벳의 아스키코드 범위(65~90)에 있는지 확인.
- 예: A=65, Z=90.
- 0~25 범위로 변환 (charCode - 65)
- 대문자의 아스키코드를 65를 기준으로 0부터 시작하는 숫자로 변환.
- 예: A=0, B=1, ..., Z=25.
- 시프트(+ n)
- 변환된 숫자에 n을 더해 새로운 위치를 계산.
- 예: A(0)에서 n=3을 더하면 3이 되어 D로 이동.
- 순환 처리 (% 26)
- 26으로 나눈 나머지를 사용해 범위를 벗어나지 않도록 순환.
- 예: Z(25)에서 n=2를 더하면 27이 되고, 27 % 26 = 1로 순환하여 B로 변환.
- 아스키코드로 복원 (+ 65)
- 다시 원래 대문자 범위로 복원하기 위해 65를 더함.
- 예: 0이 A가 되고, 25가 Z가 됨.
- 문자 반환 (String.fromCharCode)
- 최종 계산된 아스키코드를 문자로 변환해 반환.
cf.
아스키코드 ↔ 문자
: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
문자 ↔ 아스키코드
: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode
'내일배움 정리 > JS 문법 공부' 카테고리의 다른 글
숫자타입 (1) | 2024.12.25 |
---|---|
isNaN (0) | 2024.12.25 |
js 코드 최적화 (0) | 2024.12.21 |
알파벳 대소문자 변경 (1) | 2024.12.20 |
캔버스(canvas)를 이용한 도형 그리기 (0) | 2024.12.19 |