룬 알고리즘이란?

참고: 위키백과 Luhn algorithm
  • IBM 과학자인 Hans Peter Luhn이 만든 알고리즘으로, 그의 이름을 따서 룬 알고리즘이라 이름 지어졌다.
  • mod(modulus) 10 알고리즘이라고도 한다.
  • 신용카드 번호 등 다양한 식별 번호를 검증하는 데 사용되는 공식이다.

*** 2022.07.20 업데이트 ***

삼성카드 법인카드의 카드 번호는 룬 알고리즘을 따르지 않고 있어 룬 알고리즘으로 유효성을 판별할 수 없습니다!!

 

프론트엔드에서 어떤 경우에 사용하는지?

결제 수단을 등록하거나 결제를 하기 위해 신용카드 정보를 입력하는 폼을 만들 때, 사용자가 입력한 카드 번호가 (1차적으로) 유효한 카드 번호인지 확인하고 싶을 때 사용할 수 있다.

  • 유효한 카드 번호인지 확인할 수 있는 이유는, 거의 대부분의 카드사에서 발급한 카드들의 번호는 룬 알고리즘에 따라 구성되어 있기 때문이다. 카드 종류에 따라 룬 알고리즘으로 유효성을 판별할 수 없는 경우도 있는데, 그 외의 대부분의 경우에는 룬 알고리즘으로 검증할 수 있다. (참고: 위키백과 Payment card number)
    • 참고로 어떤 카드사에서 발급했는지는 카드 번호 앞 두자리로 판별이 가능하다. (위 위키백과 링크 참고) 이 점을 활용하여 프론트엔드에서는 사용자가 카드번호를 2자리까지 입력한 시점에 아메리칸 익스프레스 카드인지 등을 확인해서 15자리를 받을 것인지 16자리를 받을 것인지 분기 처리(14자리 등도 있지만 국내의 경우 잘 없는 것으로 알고 있다.)를 할 수 있다.
  • '1차적으로'라고 표현한 이유는, 카드 번호 규칙이 룬 알고리즘을 잘 따르고 있다고 해서 그 카드가 실재하는 카드인지, 또 사용자가 입력한 다른 정보들(예: 카드 비밀번호 앞 2자리)과 일치하는지 여부까지는 프론트 단에서는 확인할 수 없기 때문이다.
  • 국내에서 발급되는 카드들은 거의 대부분 16자리인데, 아메리칸 익스프레스 카드처럼 15자리인 경우도 있다. 16자리든 15자리든 상관 없이 동일한 방법으로 검증할 수 있다. (어떻게 하는지는 아래에서 설명!)

 

룬 알고리즘을 사용한 카드 번호 검증 방법

(절대 어렵지 않답니다!?)

 

다음의 예시 카드 번호를 참고해서 차근차근 따라해보자 -ㅅ-

16자리나 15자리나 상관 없으니 마음에 드시는 걸로 고고..

(카드 번호 출처: BlueSnap의 테스트용 카드 번호 목록)

[16자리] 4263982640269299

[15자리] 378282246310005

 

1. 카드 번호 중 가장 마지막 자리에 있는 숫자 "잠깐" 제거하기 (곧 다시 볼 일이 있기 때문에 아예 버려버리면 안됨!)

4 2 6 3 9 8 2 6 4 0 2 6 9 2 9 9
↓
4 2 6 3 9 8 2 6 4 0 2 6 9 2 9

 

2. 번호 뒤집기!

4 2 6 3 9 8 2 6 4 0 2 6 9 2 9
↓
9 2 9 6 2 0 4 6 2 8 9 3 6 2 4

 

3. 홀수번째(1, 3, 5번째...)에 있는 번호들은 2로 곱하기

9 2 9 6 2 0 4 6 2 8 9 3 6 2 4
↓
18 2 18 6 4 0 8 6 4 8 18 3 12 2 8

 

4. 9보다 큰 숫자에선 9를 빼주기

18 2 18 6 4 0 8 6 4 8 18 3 12 2 8
↓
9 2 9 6 4 0 8 6 4 8 9 3 3 2 8

 

5. 모든 숫자들 다 더하기

9 2 9 6 4 0 8 6 4 8 9 3 3 2 8
↓
9 + 2 + 9 + 6 + 4 + 0 + 8 + 6 + 4 + 8 + 9 + 3 + 3 + 2 + 8 = 81

 

6. 5.에서 구한 합과, 맨 처음에 1.에서 제거해놨던 그 마지막 번호를 더해주기

{5.에서 구한 합} + {1.에서 제거했던 마지막 숫자} = ?

81 + 9 = 90

 

7. 6.에서 구한 합이 10의 배수인지(=10으로 나누어 떨어지는지, =10으로 나눴을 때의 나머지가 0인지) 확인하기

70 % 10 = 0

 

8. 10의 배수가 맞다면 유효한 카드 번호인 것! 🎉🎉

 

자바스크립트 코드로 표현하기

아래 코드 말고 다른 방식으로도 얼마든지 구현할 수 있을 것이다. (여러분의 수학 실력을 뽐내보세요..)
const isValidCardNumber = (cardNumber = '') => {
  if (!cardNumber.length) return;

  // 원활한 조작을 위해 배열로 변환
  let cardNumberArray = Array.from(cardNumber);

  // 1. 카드 번호 배열에서 마지막 자리에 있는 숫자 제거하고,
  // 이 제거된 번호는 따로 킵해두기
  const lastNumber = Number(cardNumberArray.pop());

  // 2. 번호 뒤집기
  // reverse()는 원본 배열을 변환한다.
  cardNumberArray.reverse();

  // 3. 홀수번째에 있는 번호들은 2로 곱하기
  // 인덱스로는 0, 2, 4 ... -> 즉 인덱스가 짝수인(=2로 나누어 떨어지는) 애들
  cardNumberArray = cardNumberArray.map((num, idx) => idx % 2 === 0 ? Number(num) * 2 : Number(num));

  // 4. 9보다 큰 숫자에선 9를 빼주기
  cardNumberArray = cardNumberArray.map((num) => num > 9 ? num - 9 : num);

  // 5. 모든 숫자들 다 더하기
  let sum = cardNumberArray.reduce((acc, curr) => acc + curr, 0);

  // 6. {5.에서 구한 합} + {1.에서 킵해뒀던 마지막 숫자}
  sum += lastNumber;

  // 7. 6.에서 구한 합이 10의 배수인지 확인하기
  const modulo = sum % 10;

  // 8. 10의 배수가 맞다면(=나머지 값이 0이라면) 유효하고(true), 아니라면 유효하지 않은(false) 번호!
  return !modulo;
};

console.log(isValidCardNumber('4263982640269299')); // true
console.log(isValidCardNumber('378282246310005')); // true

console.log(isValidCardNumber('4263982640269229')); // false
복사했습니다!