두 수를 인자로 받고, 리턴값으로 두 수의 나머지를 표현하는 함수를 작성하라


놀랍겠지만, 나는 수학적으로 나머지를 구하는 방법을 알지 못했다. 나머지에 대해서 생각해본것이 초등학교 1학년? 2학년까지 였지 싶다. 그래서 이 문제를 만나고 적지 않게 당황했다.
그래도 수학을 좋은 선생님께 배우긴 했던 것 같다. ‘모르면 대입하라고.’ 선생님은 말했다. 맞다. 대입을 해보면 된다. 경우의 수를 찾고 경우에 따른 모든 실행을 진행하면 된다. 그리고 규칙을 발견하면 ‘유레카!’


그래서 풀어낸 나의 첫 번째 코드는 아래와 같다. (길고, 무식하지만 우직하달까.)

function modulo(num1, num2) {
  // 1. 둘 중 하나가 NaN 인지 아닌지
  if (num1 === NaN || num2 === NaN){
      return NaN;
  } else {
      // 2. num1, num2 중 하나가 0 일때
      if (num1 === 0) {
          return 0;
      } else if (num2 === 0) {
          return NaN;
      } else if (num1 === 1){
         return 1;
      } else if (num1 === -1){
          return -1;
      } else if (num1 === num2){
          return 0;
      } else if (num1 > num2) {
          if (num1 > 0 && num2 < 0) {
              let numPlus2 = num2 * -1
              return (num1 - Math.floor(num1/numPlus2) * numPlus2) ;
          } else if (num1 > 0 && num2 > 0){
              return num1 - Math.floor(num1/num2) * num2;
          } else if (num1 < 0 && num2 < 0){
              return num1;
          }
      } else if (num1 < num2){
          if (num1 > 0 && num2 > 0){
              return num1
          } else if (num1 < 0 && num2 > 0){
              let numPlus = num1 * -1;
              return (numPlus - Math.floor(numPlus/num2) * num2) * -1;
          } else if (num1 < 0 && num2 < 0){
              let numPlus = num1 * -1;
              let numPlus2 = num2 * -1;
              return (numPlus - Math.floor(numPlus/numPlus2) * numPlus2) * -1;
          }
      }
  }
}

-> 와우. 이게 뭔가. 모든 과정을 다 경우로 따져서 계속 반복하여 콘솔에 값을 입력했다. 그래도 얻은 것은 나머지를 이해했다는 사실이다.


그래서 아래와 같은 코드 리펙토링 과정을 거치게 되었다. 이 모든 것은 경우의 수를 모두 따진 결과라고 할 수 있다.

문제를 쪼개서 풀어보자 1. - 두 개의 수를 인자로 받고, NaN을 결과로 뱉는 함수를 먼저 설정하자.

function getRest (num1, num2){
   // 단계 1. num2가 NaN or 0 이라면 나누기 자체가 성립하지 않아!
   if (num2 === NaN || num2 === 0) {
      return NaN;
   }


}


문제를 쪼개서 풀어보자 2. - 절대값을 취해주고, 그 전에 결과에 +/- 를 변환하는 매직을 하나 쓰자!

function getRest (num1, num2){
   // 단계 1. num2가 NaN or 0 이라면 나누기 자체가 성립하지 않아!
   if (num2 === NaN || num2 === 0) {
      return NaN;
   }

   // 단계 2. 결과값에 +/-를 자동으로 붙여줄 매직을 하나 부려본다.
   // 삼항 조건 연산자를 이용해서 인자로 들어온 num1, num2 값에 대한 +/- 설정을 해준다.
   let plusMinus = num1 > 0 ? 1 : -1 

   // 단계 3. 우리는 큰 수에서 계속 작은 수를 빼야하기 때문에 절대값을 취해야 한다.
   num1 = Math.abs(num1)
   num2 = Math.abs(num2)
}

-> 단계 2를 단계 3보다 위에 작성한 것은 절대값을 취하면 모든 수는 양수가 되기 때문이다.

문제를 쪼개서 풀어보자 3. - 조건안에서 나머지를 구하는 반복을 취하고 값을 내자

function getRest (num1, num2){
   // 단계 1. num2가 NaN or 0 이라면 나누기 자체가 성립하지 않아!
   if (num2 === NaN || num2 === 0) {
      return NaN;
   }

   // 단계 2. 결과값에 +/-를 자동으로 붙여줄 매직을 하나 부려본다.
   // 삼항 조건 연산자를 이용해서 인자로 들어온 num1, num2 값에 대한 +/- 설정을 해준다.
   let plusMinus = num1 > 0 ? 1 : -1 

   // 단계 3. 우리는 큰 수에서 계속 작은 수를 빼야하기 때문에 절대값을 취해야 한다.
   num1 = Math.abs(num1)
   num2 = Math.abs(num2)

   // 단계 4. 조건이 있는 반복문? -> while!
   // 큰 수가 num2 보다 작아지기 전까지 반복하자!
   while (num1 >= num2) {
      num1 = num1 - num2
   }

   // 단계 5. 결과에는 +/- 구분이 필수!
   return num1 * plusMinus
}


결과적으로 위의 정답같은 코드를 이해하는 과정까지 많은 시간이 필요했다. 왜 plusMinus의 위치가 반드시 절대값 위인지를 알기 위해서 모든 경우를 다시 콘솔에 찍었다. ;; 그래도 그 과정이 좋은 과정이었음을 의심하지 않기로 했다!