[코딩테스트] 프로그래머스 - 신규 아이디 추천(JAVA)
입력 받은 아이디를 체크하여 규칙에 맞게 추천하는 프로그램 개발 7단계의 규칙을 적용해야 하는데 그 내용은 아래와 같다.
제한사항
- 1단계 new_id의 모든 대문자를 대응되는 소문자로 치환합니다.
- 2단계 new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다.
- 3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다.
- 4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다.
- 5단계 new_id가 빈 문자열이라면, new_id에 “a”를 대입합니다.
- 6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다. 만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다.
- 7단계 new_id의 길이가 2자 이하라면, new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복해서 끝에 붙입니다.
풀이
- String Method, 정규식에 대한 이해가 있다면 쉽게 풀 수 있다.
- Optional을 이용하여 풀어보았다.
public String solution(String new_id) {
/**
* Optional을 사용하기 위해 Optional.ofNullable로 감쌉니다.
* 그럴일은 없겠지만 NPE를 회피하기위해서.. ofNullable를 선택합니다.
*/
return Optional.ofNullable(new_id)
/**
* Optional map 메서드는 mapper 함수의 반환값을 Optional 객체로 감쌉니다.
* 1단계 new_id의 모든 대문자를 대응되는 소문자로 치환합니다.
* String의 toLowerCase 메서드를 통해 문자열 소문자로 치환!
*/
.map(value -> value.toLowerCase(Locale.ROOT))
/**
* 2단계 new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다.
* 이 경우 replaceAll을 사용하는게 좋습니다.
* ^의 경우 문자열의 시작이지만 []안에 들어간 ^s는 Not의 의미를 갖습니다.
* a-z는 소문자, \d는 숫자, -_.s는 특수 문자
* *는 앞의 패턴을 0회 이상 반복한다는 의미
*/
.map(str -> str.replaceAll("[^a-z\\d\\-_.]*", ""))
/**
* 3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다.
* 2단계와 마찬가지로 replaceAll 사용
* (.)는 마침표, {2,}는 2회 이상
*/
.map(str -> str.replaceAll("\\.{2,}", "."))
/**
* 4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다.
* 2단계와 마찬가지로 replaceAll 사용
* 2단계와 다르게 ^가 [.] 바깥에 위치하니 문자열의 시작
* |는 or의 의미, [.]$는 문자열의 끝이 (.)로 끝납니다.
*/
.map(str -> str.replaceAll("^[.]|[.]$", ""))
/**
* 5단계 new_id가 빈 문자열이라면, new_id에 "a"를 대입합니다.
* String의 isBlank()를 이용하면 단숨에 해결, 빈 값 여부를 체크합니다.
*/
.map(str -> str.isBlank() ? "a" : str)
/**
* 6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다.
* 만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다.
* simple if문을 넣어 16자 이상이면 substring, 그외에는 그대로 return
*/
.map(str -> str.length() >= 16 ? str.substring(0, 15) : str)
/**
* 그리고 마침표 [.]$로 끝나면 변환
*/
.map(str -> str.replaceAll("[.]$", ""))
/**
* 7단계 new_id의 길이가 2자 이하라면, new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복해서 끝에 붙입니다.
* 6단계와 마찬가지로 simple if문 추가
* 마지막 문자를 파싱 후 반복해서 3자리를 만들어야 합니다.
* for문을 이용화면 너무 코드가 지저분해지고.. 이럴떄 repeat 메서드를 이용하면 편합니다.
* str.repeat(N)는 str을 N번 반복하여 concat한 값을 return 합니다.
*/
.map(str -> str.length() <= 2 ? str + str.substring(str.length() - 1)
.repeat(3 - str.length()) : str)
/**
* optinal은 get()으로 끝 마무리 지으면 뭔가 찝찝합니다.. 인텔리제이를 사용하고 있으면 경고 문구가 노출되네요..
* 기본적으로 Optional.orElseThrow로 값이 없을 경우 에러를 생성하는 형태를 선호하나 코딩테스트에 맞게 깔끔하게 할려면 Stream으로 변환합니다.
*/
.stream()
.collect(Collectors.joining());
}
주석을 뺀 코드도 따로 공개합니다~!
public String solution(String new_id) {
return Optional.ofNullable(new_id)
.map(value -> value.toLowerCase(Locale.ROOT))
.map(str -> str.replaceAll("[^a-z\\d\\-_.]*", ""))
.map(str -> str.replaceAll("\\.{2,}", "."))
.map(str -> str.replaceAll("^[.]|[.]$", ""))
.map(str -> str.isBlank() ? "a" : str)
.map(str -> str.length() >= 16 ? str.substring(0, 15) : str)
.map(str -> str.replaceAll("[.]$", ""))
.map(str -> str.length() <= 2 ? str + str.substring(str.length() - 1)
.repeat(3 - str.length()) : str)
.stream()
.collect(Collectors.joining());
}
검증 및 보강 — 2026-10-24
문자열 변환 순서는 바꾸지 않습니다. 특히 15자 절단 뒤 끝의 마침표를 다시 제거해야 합니다. "...!@BaT#*..y.abcdefghijklm" → "bat.y.abcdefghi", "z-+.^." → "z--", "=.=" → "aaa"를 확인하세요. 대소문자 변환은 기본 로캘에 영향을 받지 않도록 Locale.ROOT를 지정했습니다. isBlank()와 repeat() 때문에 이 예제는 Java 11 이상을 전제로 하며, Optional은 문제의 필수 조건이 아닙니다.
메서드 예제는 Solution 클래스 내부에 넣는 형태이며, 사용한 클래스는 java.util.Optional, java.util.Locale, java.util.stream.Collectors입니다. 필요한 import를 클래스 선언 위에 추가하세요.
댓글