1ilsang

Developer who will be a Legend

카멜 케이스에서 스네이크로 변환하는 함수 만들기

2020-02-22 1ilsangJavaScript

카멜 케이스와 스네이크 케이스간 변환을 해야하는 일이 생각외로 많다.

데이터베이스의 컬럼과 서비스 로직에서 사용하는 객체간 미묘한 케이스 차이가 그 대표적 예이다.

id | email             | user_type | is_subscribe
---------------------------------------------------------
 1 | 1ilsang@naver.com | admin     | Y

위와 같은 컬럼이 있을 때 DB 에서 값을 가져오면 user_type, is_subscribe 와 같은 값들이 떨어진다. 물론 as 로 디비에서 카멜로 변경해서 내려줘도 되지만 여기선 바로 가져쓸 때를 가정하겠다.

이때 굳이 카멜로 변경하게 될 경우 아래와 같이 된다.

const {
  id,
  email,
  user_type: userType,
  is_subscribe: isSubscribe
} = await Models.getEmailTableById(1);

이러면 컬럼마다 매핑 해주어야 하는 불편함이 있다.

그래서 간단한 변환 함수를 만들어 보았다.

/**
 * @params string
 * 	lecture -> lecture
 * 	lectureOption -> lecture_option
 * 	lectureOptionType -> lecture_option_type
 *
 * 	else: throw new Error
 */
stringCamelToSnake(str) {
  // string 객체가 아닌 녀석들을 걸러줍니다.
  if (!str || typeof str !== 'string') throw new Error('Input type must be an [String]');

  // 카멜 케이스 공식에 어긋나는 녀석들을 걸러줍니다: 영숫자로 되어 있지 않으며 대문자가 연속되는 녀석들(e.x. leCTURe, Lec, leC)
  const dustPattern = str.match(/[^a-zA-Z0-9]|[A-Z][A-Z]+/g) || [];
  const edgeCase = str[0].charCodeAt(0) < 97 || str[str.length - 1].charCodeAt(0) < 97;
  if (dustPattern.length > 0 || edgeCase) throw new Error('Input string is not Camel case.');

  const splitStringArray = [];
  let prevIdx = 0;
  Array.from(str).forEach((e, idx) => {
    if(e.charCodeAt(0) < 97) {
      const value = `${str.slice(prevIdx, idx)}_${e.toLowerCase()}`;
      splitStringArray.push(value);
      prevIdx = idx + 1;
    }
  });

  // 대문자 기준으로 넣었기 때문에 마지막 대문자 이후의 나머지들을 푸쉬 해줍니다.
  splitStringArray.push(str.substring(prevIdx));
  // [result, _query, _items] => result_query_items
  const ret = splitStringArray.join('');

  return ret;
}

원래는 matchall 를 사용해 iterator로 만들려고 했는데 노드 10에선 안된다 -_-; 그래서 걍 보편적으로 사용할 수 있게 forEach로 돌렸다.

현재는 string 값만 변환하고 있는데 객체의 property를 변환하고 싶다면 동일한 로직에서 Object.keys()를 순회하면서 새로운 객체에 담아주면 된다.

그럼 이만.