문제 정의
첫 번째 인수로 문자열을, 두 번째 인수로 숫자를 받는 JavaScript 함수를 작성해야 합니다.
이 함수는 문자열에 포함된 각 알파벳을 주어진 숫자만큼 알파벳 순서상 뒤로 밀어 새로운 문자로 치환해야 합니다.
여기서 중요한 조건은 이동 결과가 알파벳 범위를 벗어날 때 다시 처음이나 끝으로 순환(wrap around)되어야 한다는 점입니다. 예를 들어 z 다음에는 undefined 같은 빈 값이 아니라 a가 이어져야 하며, 반대로 a보다 앞으로 밀어야 할 때는 z로 연결됩니다. 이 방식은 고전 암호학에서 널리 알려진 시저 암호(Caesar Cipher)와 정확히 같은 원리입니다.
예제 코드
다음은 위 요구 사항을 만족하는 전체 코드입니다.
const str = 'this is a str';
const encryptString = (str = '', num = 1) => {
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('');
str = str.toLowerCase();
let res = '';
for (let i = 0; i < str.length; i++) {
const letter = str[i];
// 알파벳이 아닌 문자(공백, 숫자, 기호 등)는 그대로 유지
if (alphabet.indexOf(letter) === -1) {
res += letter;
continue;
}
let index = alphabet.indexOf(letter) + num % 26;
if (index > 25) {
index -= 26;
}
if (index < 0) {
index += 26;
}
if (str[i] === str[i].toUpperCase()) {
res += alphabet[index].toUpperCase();
} else {
res += alphabet[index];
}
}
return res;
};
console.log(encryptString(str, 4));출력 결과
콘솔에 출력되는 결과는 다음과 같습니다.
xlmw mw e wxv
'this is a str'의 각 알파벳이 4칸씩 뒤로 밀려 'xlmw mw e wxv'로 바뀐 것을 확인할 수 있습니다. (t→x, h→l, i→m, s→w)
코드 동작 원리
함수가 어떻게 동작하는지 단계별로 살펴보겠습니다.
- 알파벳 기준 배열 생성: 소문자 a부터 z까지의 문자열을 split('')으로 한 글자씩 나눠 배열로 만듭니다. 이 배열의 인덱스를 기준으로 글자를 치환합니다.
- 소문자 통일: 입력 문자열을 toLowerCase()로 모두 소문자로 변환해 인덱스 계산을 단순하게 만듭니다.
- 알파벳 외 문자 처리: 공백이나 특수문자처럼 알파벳 배열에 없는 문자는 indexOf()의 결과가 -1이므로 그대로 결과에 추가한 뒤 건너뜁니다.
- 순환(랩어라운드) 처리: 현재 인덱스에 이동량(num % 26)을 더한 값이 25를 초과하면 26을 빼고, 음수가 되면 26을 더해 항상 0~25 범위 안에 머물도록 합니다. 덕분에 z에서 a로, a에서 z로 자연스럽게 순환하며, 음수 이동(복호화)도 가능합니다.
- 결과 조합: 치환된 글자들을 하나의 문자열로 이어 붙여 최종 결과로 반환합니다.
참고: 대소문자 유지
위 코드는 처리 전에 문자열 전체를 소문자로 변환하기 때문에 출력은 항상 소문자로 반환됩니다. 입력의 대소문자를 그대로 유지하고 싶다면 각 글자의 원래 대소문자 여부를 변환 전에 저장해 두었다가 결과에 반영하면 됩니다.