일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- MySQL
- JPQL
- Java
- JPA
- backend
- RequestBody
- OS
- Test
- Junit 5
- Server
- proxyFactory
- 소프티어
- Service 계층 테스트
- modelmapper
- ExceptionResolver
- 공룡책
- Spring
- Coputer Science
- 자바
- 일상
- Test code
- ObjectMapper
- 테크쇼
- mapstruct
- db
- FCM
- enumSet
- softeer
- Test Doulbe
- 인프콘2023
- Today
- Total
공부내용공유
Java Random, SecureRandom, RandomStringUtil 살펴보기 본문
서론
어떤 기능을 구현하면서 글자 수가 제한되어 있는 중복되면 안되는 랜덤 문자열을 만들어야했다, 글자 수가 제한이 안되어 있다면 uuid, hash 등 다양한 방식을 편하게 사용할 수 있었지만 그렇지 못했기에 이것 저것 찾아보았다.
이번 글에서는 찾아보면서 알게된 랜덤 값을 만드는 클래스, 클래스들의 특징등을 정리할 예정이다.
본론
해당 글은 자바에 구현되어져있는 클래스와 라이브러리를 다룬다. 다른 언어를 사용하는 사람들에게는 큰 도움이 되지 않을 것 같다.
글의 목차는
- 랜덤하고 유니크한데 길지는 않은 쌈@뽕한 문자열
- Random
- Apache Common (RandomString, RandomUtil ...)
- Secure Random
이렇게 구성될 예정이다.
1. 랜덤하고 유니크한데 길지는 않은 쌈@뽕한 문자열
내가 만들어야하는 랜덤한 문자열은
- 글자 수가 10자리 안팎이어야 한다.
- 현재 사용중인 문자열 중 유니크해야 한다.
- 삭제되어도 일정 기간동안 사용했던 문자열을 저장하고 그 삭제된 문자열들과도 겹치면 안된다.
이러한 특징을 가지고 있었다, 사실 유니크하다는 조건이 있어서 MongoDB의 ObjectId와 같은 방식을 사용해도 좋겠다 싶었는데 글자 수 때문에 쉽지 않았고 uuid나 hash 함수도 사용하기에 결과물 글자 수가 너무 길었다.
그래서 결국 숫자, 알파벳으로 이루어진 랜덤한 문자열을 직접 만드는 방식을 선택했고 최대한 안겹쳐게 하기 위해 랜덤 문자열을 만드는 라이브러리들을 찾아보았다.
2. Random
자바를 처음 공부할 때 다들 한번씩은 접한 클래스일 것이다. 간단하게만 소개를 하자면
public int nextInt(int bound)
public long nextLong()
public boolean nextBoolean()
이러한 사용자가 원하는 타입의 랜덤한 값을 반환하는 메서드를 제공해주고
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
public Random(long seed) {
if (getClass() == Random.class)
this.seed = new AtomicLong(initialScramble(seed));
else {
// subclass might have overridden setSeed
this.seed = new AtomicLong();
setSeed(seed);
}
}
public synchronized void setSeed(long seed) {
this.seed.set(initialScramble(seed));
haveNextNextGaussian = false;
}
위 생성자와 setSeed
메서드에서 볼 수 있듯이 랜덤한 값을 생성할 때 기준이 되는 seed 값을 설정할 수 있다.
Random 클래스의 주석을 보면
If two instances of Random are created with the same seed, and the same sequence of method calls is made for each,
they will generate and return identical sequences of numbers.
seed가 같다면 랜덤 값이 동일한 순서로 나오게 되고 위 생성자 코드에서 볼 수 있듯이 nano time을 기반으로 seed를 만든다.
즉 Random 값을 생성할 때 유니크한 seed를 지정해주지 않는 상황에서는 같은 seed를 가진 Random이 만들어지고 각 Random 클래스는 완전히 동일한 순서로 같은 값을 뽑아내게 된다.
확률은 적지만 내가 구현하는 기능에서 이런 일이 일어나면 치명적인 오류가 될 수 있기에 다른 클래스들을 알아 보았다.
3. Apache randomString
검색을 했을 때 apache common에서 제공하는 여러 클래스들도 사용하는 예시가 많았고 프로젝트에서도 이미 apache common을 사용하고 있어서 클래스를 알아보았다.
public static String random(int count, int start, int end, boolean letters, boolean numbers) {
return random(count, start, end, letters, numbers, (char[])null, RANDOM);
}
해당 클래스는 위 코드에서 볼 수 있듯이 숫자, 알파벳을 랜덤으로 섞어서 값을 만들어준다.
하지만
public class RandomStringUtils {
private static final Random RANDOM = new Random();
public RandomStringUtils() {
}
해당 라이브러리도 Random 클래스를 사용한다.. 결국 아까와 같은 위험에 노출된다. 코드의 예시로는 RandomStringUtils를 가져왔지만 RandomUtil 같은 경우도 Random을 사용하고 있으니 주의하여 사용하자.
4. SecureRandom
물론 위에서 언급한 상황이 발생할 확률은 정말 적지만 SecureRandom은 그런 상황에서도 완전한 난수를 반환하기에 이 클래스를 사용하기로 결정하였다.
Additionally, SecureRandom must produce non-deterministic output.
Therefore any seed material passed to a SecureRandom object must be
unpredictable, and all SecureRandom output sequences must be cryptographically strong
클래스 주석에서 볼 수 있듯이 같은 seed로 만들어져도 완전한 난수가 나온다.
SecureRandom의 경우에는 Os에서 seed 값을 가져오는데 linux에서 실행될시 /dev/random 에서 값을 가져오고 이 때 퍼포먼스 이슈가 있을 수 있다고 한다 스택오버플로우 해당 글에서 해당 이슈를 자세히 다루고 해결책도 나와있으니 참고하면 좋을 것 같다.
결국 결론은 seed를 가져올 때 /dev/random에서 가져올 시 blocking으로 인해 오랜 시간이 걸릴 수 있으니 /dev/urandom에서 가져오는게 웬만하면 좋고 new SecureRandom() 으로 생성할 경우 /dev/urandom 을 사용하니 그렇게 생성하자 혹은 다른 알고리즘을 사용하자이다. 참고한 블로그 (jdk 1.8 기준이긴 하다.)
해당 방식으로 생성하고 테스트를 몇번 해봤는데 성능 관련 문제는 없어서 SeucreRandom 클래스 + defaultPRNG 알고리즘을 사용하기로 하였다.
결론
그렇게 깊고 어려운 내용은 아니지만 예전에 아무생각 없이 사용하던 Random 클래스에 대해 좀 더 잘 알게되어서 간단하게 정리하였다.
난수를 직접 만들어야 하는일이 있는 사람이라면 해당 내용을 참고해서 만들면 좋을 것 같다!
'Server > Java' 카테고리의 다른 글
java inner 클래스 static? non-static? (이펙티브 자바 item 24) (0) | 2024.07.14 |
---|---|
java 태그 달린 클래스 vs 계층 구조 (feat : 오브젝트 chap 7 객체 분해) (0) | 2024.07.07 |
java package private (feat : default 접근 제어자) (0) | 2024.06.09 |
Value object Null 값 처리하기 (feat: null object pattern) (0) | 2024.06.02 |
java testFixture 는 어디에 둘까 (feat: package - private fixture) (0) | 2024.05.25 |