Post

랜덤 문자열 생성 함수 리팩토링 - 가독성과 확장성을 높이는 방법 🛠️

랜덤 문자열 생성 함수 리팩토링 - 가독성과 확장성을 높이는 방법 🛠️

간단한 랜덤 문자열 생성 함수가 필요해 빠르게 코드를 작성했다가, 좀 더 유지보수가 쉽고 깔끔한 코드로 개선하고 싶다는 생각이 들었다. 랜덤한 영문 소문자와 숫자를 각각 뽑아내는 기능을 요구사항으로 두고 리팩토링을 시작했다

처음 버전

처음 작성한 코드는 기능적인 문제 없이 잘 동작했다. generateRandomStringgenerateRandomNumberString 두 개의 함수를 만들어 각기 다른 문자셋을 관리하는 방식이었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const generateRandomString = (length) => {
  const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  let result = '';
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * characters.length));
  }
  return result;
};

const generateRandomNumberString = (length) => {
  const characters = '0123456789';
  let result = '';
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * characters.length));
  }
  return result;
};

하지만 코드를 보면서 아쉬움을 느꼈다. 두 함수가 거의 동일한 로직을 반복하고 있어, ‘중복 코드’가 눈에 띄었다. 이대로 두면 나중에 다른 종류의 랜덤 문자열(예: 특수문자 포함)이 필요해질 경우, 새로운 함수를 또 만들어야 할 것이다.

바뀐 버전

이러한 문제를 해결하기 위해 객체를 활용해 문자셋을 관리하는 방식으로 코드를 개선했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const dictionary = {
  'a-z': 'abcdefghijklmnopqrstuvwxyz',
  '0-9': '0123456789',
};

const generateRandomString = (type: 'a-z' | '0-9', length = 4) => {
  const characters = dictionary[type];
  let result = '';
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * characters.length));
  }
  return result;
};

위와 같이 dictionary라는 객체를 만들어 문자셋을 저장하고, 함수는 이 객체에서 원하는 타입의 문자셋을 가져와 사용하도록 변경했다. 추가적으로 length = 4로 default 값을 지정해주면서, length의 type을 any에서 number로 명확하게 지정해주는 효과도 얻었다.

리팩토링 후 얻은 네 가지 이점 👍

이번 리팩토링을 통해 얻은 가장 큰 이점은 다음과 같다.

  1. 코드 중복 제거: 이제 하나의 함수로 여러 종류의 랜덤 문자열을 생성할 수 있다. 조건문으로 분기하는 대신, 객체의 키(key)를 통해 원하는 값을 바로 가져오는 방식이 훨씬 깔끔하다.

  2. 가독성 향상: a-z, 0-9와 같은 직관적인 키 이름 덕분에 어떤 문자셋을 사용하는지 코드만 봐도 바로 이해할 수 있다.

  3. 뛰어난 확장성: 나중에 특수문자나 대문자 등 새로운 타입의 랜덤 문자열이 필요해도, dictionary 객체에 새로운 키-값 쌍만 추가해주면 된다. 기존 함수 로직은 전혀 수정할 필요가 없다.
  4. 타입 안정성 확보: length = 4와 같이 매개변수에 기본값을 설정함으로써, 함수 호출 시 length 인자를 생략할 수 있게 되었다. 더불어 length의 타입이 any에서 number로 명확하게 지정되어 함수의 안정성이 향상되는 이점도 얻었다.

추가로 생각해볼 점

1
'a-z' | '0-9'

대신

1
keyof typeof dictionary

를 사용하면 어떨까?

1
2
3
4
5
6
7
8
9
10
11
12
13
const dictionary = {
  'a-z': 'abcdefghijklmnopqrstuvwxyz',
  '0-9': '0123456789',
};

const generateRandomString = (type: keyof typeof dictionary, length = 4) => {
  const characters = dictionary[type];
  let result = '';
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * characters.length));
  }
  return result;
};

이렇게 작성하면 새로운 문자열 셋이 추가되더라도, generateRandomString 함수를 수정할 필요가 없어진다. 따라서 원래 방식처럼 타입을 따로 쓰는 것보다 유지보수가 쉬워진다.

This post is licensed under CC BY 4.0 by the author.