보안팀에서 “사내 채팅 로그를 외부로 공유할 때, 원문을 그대로 노출하지 않으면서도 복호화 가능한 형태로 바꾸는 도구”를 만들기로 했습니다.
이 도구는 입력 문장을 랜덤 치환(Substitution) 암호로 변환하고, 같은 키로 복호화 검증까지 수행합니다.
이번 미션은 아래 요구사항에 맞게 치환 키 생성, 암호화/복호화, 통계 리포트 출력을 구현하는 것입니다.
- 터미널에서
java -version을 실행하여 Java 버전이 21인지 확인한다. - Mac 또는 Linux 사용자는
./gradlew clean test명령을 실행한다. - Windows 사용자는
gradlew.bat clean test또는./gradlew.bat clean test명령을 실행한다.
- 기능을 구현하기 전
README.md에 구현할 기능 목록을 정리해 추가한다. - Git의 커밋 단위는
README.md에 정리한 기능 목록 단위로 추가한다. - 커밋 메시지는 AngularJS Commit Message Conventions을 참고해 작성한다.
프로그램을 실행하면 아래 순서대로 입력을 받는다.
- 원문 메시지 입력
- 알파벳 처리 모드 입력
- 숫자 처리 모드 입력
- 키 미리보기 출력 개수 입력
- 랜덤 치환 키 생성
- 암호문/리포트 출력 + 복호화 검증 출력 후 종료
- 1~200자
- 허용 문자
- 영문 대소문자:
A-Z,a-z - 숫자:
0-9 - 공백: space 한 칸
- 구두점:
. , ! ?
- 영문 대소문자:
- 위 허용 문자 외의 문자가 포함되면 예외 처리한다.
E: 대소문자 구분(Extend)A-Z는A-Z범위 안에서 치환a-z는a-z범위 안에서 치환
I: 대소문자 미구분(Insensitive)- 입력 메시지의 모든 영문은 소문자로 통일한 뒤 치환한다.
- 출력 암호문도 영문은 모두 소문자로 출력한다.
K: 숫자 유지(Keep) —0-9는 그대로 둔다.S: 숫자 치환(Substitute) —0-9도 랜덤 치환 키를 만들어 치환한다.
- 5~20 사이의 정수
- 암호문 출력 후, 치환 키의 일부를 “미리보기”로 출력한다.
- 미리보기는 원본 문자 기준 오름차순으로 정렬하여 앞에서부터 N개를 출력한다.
- 반드시
camp.nextstep.edu.missionutils.Randoms.pickNumberInRange()를 사용하여 랜덤 값을 생성한다. - 치환은 중복 없이 1:1로 매핑되어야 한다(순열).
- 알파벳 처리 모드에 따라 키의 대상이 달라진다.
E(대소문자 구분)- 대문자 키:
A-Z(26개) - 소문자 키:
a-z(26개)
- 대문자 키:
I(대소문자 미구분)- 소문자 키:
a-z(26개)
(입력 영문을 소문자로 통일하기 때문에 대문자 키는 생성하지 않는다.)
- 소문자 키:
숫자 처리 모드가 S라면 숫자 키도 생성한다.
- 숫자 키:
0-9(10개)
- 아래 형식으로 출력한다.
암호화를 시작합니다.
암호문: <cipherText>
체크섬: <checksum>
- 암호문에서 공백(space)을 제외한 모든 문자의 ASCII 코드값을 더한 뒤,
10000으로 나눈 나머지 - 예)
checksum = (sum % 10000)
- 아래 형식으로 출력한다.
키 미리보기
- A -> Q
- B -> T
...
- 출력 개수 N만큼만 출력한다. (알파벳은 최대 26개, 숫자는 최대 10개이므로
min(N, 최대개수)만큼 출력한다.) E모드에서 대문자/소문자 키를 모두 생성한 경우:- 대문자 미리보기 → 소문자 미리보기 → (숫자 모드가 S면) 숫자 미리보기 순서로 출력한다.
I모드에서는 소문자 미리보기만 출력한다(필요 시 숫자 미리보기 추가).
- 암호문에서 공백(space)과 구두점(. , ! ?) 을 제외하고, 가장 많이 등장한 문자 3개를 출력한다.
- 등장 횟수가 같다면 문자 기준 사전순(오름차순)으로 정렬한다.
- 아래 형식으로 출력한다.
빈도 리포트 (TOP 3)
1) <char> : <count>
2) <char> : <count>
3) <char> : <count>
- 생성된 키로 암호문을 복호화했을 때 원문과 동일하면
OK를 출력한다. - 원문은 모드에 의해 변형될 수 있다.
I모드라면 원문을 소문자로 통일한 값과 비교한다.
- 아래 형식으로 출력한다.
복호화 검증: OK
잘못된 입력이 들어오면 [ERROR]로 시작하는 메시지를 출력하고 해당 입력부터 다시 입력받는다.
- 원문 메시지가 규칙에 맞지 않는 경우
[ERROR] 원문 메시지 형식이 올바르지 않습니다.
- 모드 입력이 규칙에 맞지 않는 경우
[ERROR] 모드 입력이 올바르지 않습니다.
- 숫자 처리 모드 입력이 규칙에 맞지 않는 경우
[ERROR] 숫자 처리 모드 입력이 올바르지 않습니다.
- 키 미리보기 출력 개수가 규칙에 맞지 않는 경우
[ERROR] 키 미리보기 출력 개수가 올바르지 않습니다.
원문 메시지를 입력해 주세요.
Hello, World!
알파벳 처리 모드를 입력해 주세요. (E:대소문자 구분, I:대소문자 미구분)
E
숫자 처리 모드를 입력해 주세요. (K:유지, S:치환)
K
키 미리보기 출력 개수를 입력해 주세요. (5~20)
8
암호화를 시작합니다.
암호문: Jxdda, Qavds!
체크섬: 1079
키 미리보기
[대문자]
- A -> Q
- B -> T
- C -> A
- D -> Z
- E -> V
- F -> W
- G -> R
- H -> J
[소문자]
- a -> q
- b -> t
- c -> a
- d -> z
- e -> x
- f -> w
- g -> r
- h -> j
빈도 리포트 (TOP 3)
1) d : 3
2) a : 2
3) J : 1
복호화 검증: OK
위 암호문/키는 예시이며, 실제 실행 결과는 랜덤에 따라 달라질 수 있다.
원문 메시지를 입력해 주세요.
Hello@World
[ERROR] 원문 메시지 형식이 올바르지 않습니다.
원문 메시지를 입력해 주세요.
Hello 2026!
알파벳 처리 모드를 입력해 주세요. (E:대소문자 구분, I:대소문자 미구분)
X
[ERROR] 모드 입력이 올바르지 않습니다.
알파벳 처리 모드를 입력해 주세요. (E:대소문자 구분, I:대소문자 미구분)
I
숫자 처리 모드를 입력해 주세요. (K:유지, S:치환)
Z
[ERROR] 숫자 처리 모드 입력이 올바르지 않습니다.
숫자 처리 모드를 입력해 주세요. (K:유지, S:치환)
S
키 미리보기 출력 개수를 입력해 주세요. (5~20)
3
[ERROR] 키 미리보기 출력 개수가 올바르지 않습니다.
키 미리보기 출력 개수를 입력해 주세요. (5~20)
20
암호화를 시작합니다.
암호문: jxddp 7879!
체크섬: 794
키 미리보기
[소문자]
- a -> q
- b -> t
- c -> a
- d -> z
- e -> x
- f -> w
- g -> r
- h -> j
- i -> v
- j -> n
- k -> b
- l -> d
- m -> o
- n -> c
- o -> p
- p -> s
- q -> u
- r -> i
- s -> f
- t -> l
[숫자]
- 0 -> 8
- 1 -> 1
- 2 -> 7
- 3 -> 2
- 4 -> 0
- 5 -> 6
- 6 -> 9
- 7 -> 4
- 8 -> 5
- 9 -> 3
빈도 리포트 (TOP 3)
1) 7 : 2
2) d : 2
3) 8 : 1
복호화 검증: OK
- JDK 21에서 실행 가능해야 한다.
- 프로그램 실행의 시작점은
Application의main()이다. System.exit()를 호출하지 않는다.- 사용자가 잘못된 값을 입력할 경우
IllegalArgumentException을 발생시키고,
[ERROR]로 시작하는 메시지를 출력한 후 해당 입력부터 다시 받는다.
camp.nextstep.edu.missionutils.Randoms의pickNumberInRange()를 사용한다.
예시
int index = Randoms.pickNumberInRange(0, candidates.size() - 1);- 과제 제출은 GitHub Repository에 push한 뒤 제출한다.
- 기능 구현 후
./gradlew clean test실행 결과가BUILD SUCCESSFUL인지 확인한다.