[js] - 시저 암호

오유민·2026년 1월 3일

프로그래머스

목록 보기
12/14

이 문제를 처음 봤을 때는 아스키 코드로 접근하는 방법 외의 다른 방법은 딱히 떠오르지 않았다.
이마저도 한 번에 풀지는 못했지만... ai의 도움을 받아 완성한 나의 코드는 다음과 같다.

문제 풀이 과정

1) 문자열 s를 하나씩 순회한다
2) 공백이면? 아무것도 하지 않는다.
3) 대문자면? 대문자 범위 (65~90) 내에서 움직이되, 90('Z')을 넘으면 65('A')로 돌아온다.
4) 소문자면? 소문자 범위 (97~122) 내에서 움직이되, 122('z')를 넘으면 97('a')로 돌아온다.
5) 변환된 값들을 배열에 담은 뒤에 'join('')'으로 합친다.

- A: 65, a: 97, 공백(" "): 32
- charCodeAt(인덱스): 문자 -> 아스키 코드로 변환하는 함수
- String.formCharCode(숫자): 아스키 코드 -> 문자로 변환하는 함수
function solution(s, n) {
    let result = [];
    
    for (let i=0; i<s.length; i++) {
        let char = s[i];
        
        // 1. 공백인 경우
        if (char === ' ') { // 빈 문자열이 아니고 공백이기 때문에 꼭 띄워줘야 함!
            result.push(' ');
            continue;
        }
        
        // 2. 아스키 코드 확인
        let code = char.charCodeAt(0); // 이미 char은 인덱스 i를 담고있는 한 글자짜리 문자이므로 0
        
        // 3. 대문자인 경우
        if (code >= 65 && code <= 90) {
            let shifted = code + n;
            if (shifted > 90) shifted -=26; // 알파벳은 총 26개이므로, 만약 범위를 넘어가는 경우 다시 돌아오게 하기
            result.push(String.fromCharCode(shifted));
        }
        
        // 4. 소문자인 경우
        if (code >= 97 && code <= 122) {
            let shifted = code + n;
            if (shifted > 122) shifted -=26;
            result.push(String.fromCharCode(shifted));
        }
    }

    return result.join('');
}

하지만 다른 사람들의 풀이를 보고 나니 아스키 코드를 다룰 필요 없이, 훨씬 더 가독성 있게 이 문제를 풀 수 있는 방법을 알게 되었다. 그건 바로 대문자와 소문자가 순서대로 적힌 문자열을 미리 만들어두는 것이다!

1) 대문자 모음("ABC...Z")과 소문자 모음("abc...z")을 변수에 각각 저장한다.
2) 현재 문자가 대문자인지 소문자인지 찾는다.
3) 현재 문자가 몇 번째 인덱스인지 찾는다.
4) 해당 인덱스에 n을 더한 뒤, 전체 길이인 26으로 나눈 나머지 위치에 있는 문자를 가져온다.

function solution(s, n) {
    const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const lower = "abcdefghijklmnopqrstuvwxyz";
    let result = "";
    
    for (let char of s) {
        // 1. 공백인 경우
        if (char === " ") {
            result += " "; // 문자열이므로 push가 아닌 덧셈 연산
            continue;
        }
        
        // 2. 현재 문자가 대문자인지 소문자인지 확인
        const textArr = upper.includes(char) ? upper : lower;
        
        // 3. 현재 문자의 위치(index)를 찾고 n만큼 이동
        // (index + n) % 26을 하면 z를 넘어가도 다시 a로 돌아옴
        let index = textArr.indexOf(char);
        let nextIndex = (index + n) % 26;
        
        result += textArr[nextIndex];
    }
    return result;
}
for (let char of s)
  • for... of: 문자열 s에서 한 글자씩 순서대로 꺼내 변수 char에 담아주는 역할을 한다.
        // 1. 공백인 경우
        if (char === " ") {
            result += " "; // 문자열이므로 push가 아닌 덧셈 연산
            continue;
        }
  • 조건문을 통해 현재 문자가 공백인지 확인하고, 공백이면 결과 문자열에 추가한다.
  • continue;를 통해 아래 코드들을 실행하지 않고, 바로 다음 char에 대한 조건문을 실행한다.
        // 2. 현재 문자가 대문자인지 소문자인지 확인
        const textArr = upper.includes(char) ? upper : lower;
  • upper.includes(char)을 통해 현재 글자가 대문자 문자열 안에 들어있는지 확인한다.
  • 만약 들어있다면 upper를 선택하고, 없다면 소문자라고 판단하여 lower를 선택해 textArr라는 변수에 담는다.
        // 3. 현재 문자의 위치(index)를 찾고 n만큼 이동
        // (index + n) % 26을 하면 z를 넘어가도 다시 a로 돌아옴
        let index = textArr.indexOf(char);
        let nextIndex = (index + n) % 26;
  • indexOf(char): 현재 글자가 알파벳 순서상 몇 번째 인덱스인지 찾는다. (예: 'A'는 0)
  • index + n: 현재 위치에서 문제에서 주어진 거리 n만큼 오른쪽으로 이동한다.
  • % 26: 이 부분이 아주 중요!
  • 만약 index + n이 26 이상이 되면(즉, Z를 넘어가면) 다시 앞(A)으로 돌아오게 만드는 장치이다.
  • 예를 들어, 'Z'(25)에서 1만큼 밀면 26이 되는데, 26 % 26은 0이 되어 다시 'A'를 가리키게 된다.
result += textArr[nextIndex];
...
return result;
  • 계산된 nextIndex 위치에 있는 글자를 textArr에서 꺼내 결과 문자열에 붙인다.
profile
개발자연습생의 개발 일기

0개의 댓글