티스토리 뷰

함수를 사용하다 보니 비슷비슷한 함수가 너무 많이 생겨 관리하기가 어려워 졌습니다.

그래서 function을 하나로 통합하고 if 문을 쓰다보니 이번에는 코드가 지저분해 지는 문제가 발생했습니다.

 

// 통합전 A
function a( val ){
  val = val * val;
  return val;
}
// 통합전 B
function b( val ){
  val = val + 10;
  return val;
}

// 통합 후
function c ( val, condition ){
  if (condition === 'A') {
    val = val * val
  } else {
    val = val + 10
  }
  return val
}

위의 A, B 함수의 경우 목적이 다른 함수라면 분리하는것이 맞습니다. 하지만 목적이 같다면 C 함수 형태로 만드는 것이 향후 코드를 이해하고 유지보수 하는데 유리합니다. ( 예를 들어 부가세 계산 함수 같은 경우 면세, 과세 조건에 따라 결과값이 달라지지만 목적은 부가세 계산 한가지 이므로, C 와 같은 형태로 만들어야 합니다. -> 물론 if 문을 쓰지 않고 간략하게 만들수도 있습니다만.. 예시이므로.. ^^; ) 

 

또한 코드가 분산되어 있을 경우에는 아래와 같은 문제가 발생하게 됩니다.

  1. 다른 부분이 작은 코드 파편이고 한 번에 몰려있지 않고 함수 내부의 코드 위 아래에 분산 되어있다면 공통 부분을 뽑아내 함수를 만들기 곤란하거나 너무 많은 함수를 만들게 된다.
  2. 성능이 대단이 중요한 함수의 경우 함수 내부에서 작은 코드 조각의 독립성을 위해 다른 함수를 부르는 부하를 갖기 곤란한 경우 그냥 함수를 여러 판 만들게 된다.

그래서 무언가 효과적인 방법은 없을까 하여 찾아본 대안으로 아래와 같이 처리해 보았습니다.

 

우선 템플릿 대상함수를 완전히 함수로 정의하되 코드 조각이 삽입될 부분을 문자열로 지정합니다.

var tempFunc = function (option) {
  '@target'
}

그 다음 function의 실제 내용이 될 코드를 string형태로 분리합니다. (꼭 아래처럼 할 필요는 없습니다. json 형태로도 되고 temp function이 위치한곳에 같이 작성해도 문제 없습니다. )

// codeString.js

export default {
  a:`return {
    val = val * val
  }
  `,
  b: `return {
    val = val + 10
  }`
}

자 이제 function의 toString 기능과 String object의 replace 기능을 이용해서 치환 로직을 작성합니다.

const code = require('./codeString.js')

var strA = tempFunc.toString().replace("'@target'", code.a)
var a = new Function('', 'return' + str)()

var strB = tempFunc.toString().replace("'@target'", code.b)
var b = new Function('', 'return' + str)()

자 일단 a와 b function이 생성되었습니다. 앗, 그런데 이렇게 할거면 뭐하러 code를 분리했나 싶습니다.

 

그래서 좀 더 바꿔보았습니다.

const code = require('./codeString.js')

var func = {}
Object.keys(code).map(x => {
  func[x] = (new Function('', 'return' + tempFunc.toString().replace("'@target'", code[x])))()
})

자 이제 func object안에는 a와 b 함수가 같이 들어가게 되었습니다.

 

또하나, new Function 을 통해 함수를 생성하는 로직은 깔끔하고 좋지만 여기에는 큰 문제가 하나 있습니다.

new Function은 스코프를 인식하지 않는다는 점입니다. 따라서 이를 보강하려면 new Function을 호출하는 시점에 인자로 보내야만 합니다. 

const code = require('./codeString.js')

var func = {}
Object.keys(code).map(x => {
  func[x] = (new Function('env', 'app', 'return' + tempFunc.toString().replace("'@target'", code[x])))(env, window.getApp)
})

위와 같이 처리할 경우 function의 로직을 아예 다른 서버나 db에 저장하는 것도 가능해 집니다. 하지만 build 시점에 검증이 되지 않는다는 문제점과 new function 이 가진 보안 취약점이 있으므로 신중히 접근할 필요가 있습니다.

 

감사합니다.

댓글