메뉴

Luhn 알고리즘: 카드 번호 한 자리 검산 원리

Luhn 알고리즘이 카드 번호의 어느 자리를 어떻게 검산하는지, 손으로 따라 하는 계산 순서와 이 검사가 걸러내지 못하는 오류까지 차근차근 설명합니다.

게시일

  • Luhn
  • 검증

카드 번호를 입력하다가 한 자리를 잘못 눌렀는데 화면이 곧바로 오류를 표시한 적이 있을 것입니다. 서버에 물어보지도 않고 어떻게 알았을까요. 답은 번호 안에 검산용 숫자가 들어 있다는 것입니다. Luhn 알고리즘은 이 검산 숫자를 계산하는 공개된 규칙이며, 카드 번호 분야에서는 국제 표준을 통해 널리 자리 잡았습니다. 이 글에서는 그 계산을 손으로 끝까지 따라 해 보고, 이 검사가 무엇을 잡고 무엇을 놓치는지 구분합니다.

번호 끝에 검산 숫자가 있는 이유

카드 번호는 사람이 종이에서 옮겨 적고, 전화로 불러 주고, 손으로 입력하는 대상입니다. 그 과정에서 한 자리를 잘못 누르는 일은 흔합니다. 서버까지 가지 않고도 이런 실수를 걸러내려면 번호 자체가 스스로를 증명할 수 있어야 하고, 그래서 마지막 한 자리를 앞의 자릿수 전체로부터 계산해 붙입니다.

이 구조는 결제망만의 발명이 아닙니다. 검산 숫자가 필요한 여러 식별 체계에서 오랫동안 쓰여 온 방식이고, 카드 번호는 그중 사람들이 가장 자주 마주치는 사례일 뿐입니다.

그렇다면 검산 규칙은 왜 카드 번호에 쓰이는 걸까요? 번호를 사람이 옮겨 적는 상황을 떠올려 보면 이유가 분명해집니다. 카드에 인쇄된 숫자를 보고 직접 입력하거나, 종이 신청서에서 옮기거나, 전화로 불러 주는 과정에서 한 자리는 반드시 틀립니다. 서버까지 요청을 보내지 않고도 이런 실수를 잡아낼 수 있다면, 사용자는 기다리지 않고 곧바로 고칠 수 있습니다.

그래서 검산 숫자는 번호를 만드는 쪽과 읽는 쪽 사이의 약속입니다. 발급사는 계좌를 만들 때 규칙에 맞는 마지막 자리를 계산해 붙이고, 검증하는 쪽은 같은 계산을 되풀이해 값이 맞는지 확인합니다. 이 약속은 결제망 전용이 아니라 검산 숫자가 필요한 여러 식별 체계에서 오랫동안 쓰여 온 방식입니다.

카드 한 장을 손으로 검산하려면 어떻게 할까?

번호가 진짜 검산을 통과하는지 직접 확인하고 싶다면 계산기를 꺼낼 필요가 없습니다. 번호의 마지막 자리를 떼어 기억해 두고, 남은 자릿수를 오른쪽에서 왼쪽으로 훑으면서 두 번째마다 값을 두 배로 만듭니다. 두 배로 만든 값이 두 자리가 되면 그 두 자리를 더해 한 자리로 줄이고, 이렇게 얻은 값과 그대로 둔 값을 모두 더합니다.

이 합계에 처음 떼어 둔 마지막 자리를 더했을 때 10의 배수가 되면 규칙을 만족합니다. 10의 배수가 아니라면 그 번호는 어딘가에서 잘못 만들어졌거나 옮겨 적는 과정에서 한 자리가 바뀐 것입니다. 몇 번 반복하면 손으로도 익숙해지므로, 자동 검증 결과가 이상할 때 원인을 좁히는 데 요긴하게 쓸 수 있습니다.

이 검사가 잡는 오류와 놓치는 오류는 어떻게 다를까?

Luhn이 잘 잡는 것은 한 자리를 잘못 누른 경우입니다. 자릿수 하나가 바뀌면 합계가 10의 배수에서 어긋나므로 거의 항상 걸립니다.

이웃한 두 자리를 바꿔 쓴 경우도 대부분 걸립니다. 다만 두 배를 적용한 결과가 같아지는 조합, 예를 들어 0과 9, 또는 2와 7처럼 뒤집어도 합이 유지되는 짝은 통과할 수 있습니다. 그래서 인접 자리 뒤바꿈을 백 퍼센트 잡는다고 말하면 안 됩니다.

놓치는 것은 훨씬 많습니다. 존재하지 않는 계좌, 한도를 넘긴 카드, 분실 신고된 카드, 남의 카드 모두 이 검사와 무관합니다. 이러한 판단은 발급사 승인 응답의 영역입니다.

통과했다는 말은 무엇을 뜻할까?

검증을 통과했다는 문장은 흔히 오해를 부릅니다. 여기서 확인된 것은 오직 하나, 번호의 자릿수가 검산 규칙을 만족한다는 사실입니다. 그 번호가 발급되었는지, 결제가 가능한지는 전혀 말해 주지 않습니다.

그래서 이런 문장은 피하는 편이 좋습니다. 이 번호는 유효한 카드입니다, 같은 표현 대신 형식 검사를 통과한 번호입니다라고 적는 것이 정확합니다. 유효하다는 단어는 화면 문구에서도 승인 성공처럼 읽히기 쉽습니다.

검산을 통과하면 결제가 될까?

결제가 되지 않습니다. 이 질문은 검산 규칙을 가장 자주 오해하는 지점이라 따로 짚어 둘 만합니다. 검산이 확인하는 대상은 번호가 스스로 정한 산술 약속을 지켰는지 여부 하나뿐입니다. 그 번호가 실제로 발급된 계좌를 가리키는지, 한도가 남아 있는지, 분실 신고가 되어 있는지는 검산과 아무 관계가 없습니다.

결제가 성립하려면 발급사의 승인 응답이 필요합니다. 검산은 그 요청을 보내기 전에 형식을 걸러내는 단계일 뿐이고, 통과했다고 해서 요청의 결과를 예측할 수는 없습니다.

구현할 때 자주 틀리는 지점은 어디일까?

  • 어느 쪽 끝에서 두 배를 시작하는지 헷갈리는 경우: 검산 숫자는 두 배 대상이 아니어야 합니다. 한 칸 어긋나면 멀쩡한 번호가 전부 거부됩니다.
  • 숫자가 아닌 문자를 먼저 걷어내지 않는 경우: 사용자는 공백과 하이픈을 자연스럽게 입력합니다.
  • 빈 입력이나 한두 자리 입력을 통과시키는 경우: 자릿수 검사가 검산보다 앞에 와야 합니다.
  • 검산만 하고 끝내는 경우: 시작 숫자로 카드사를 확인하는 단계가 빠지면 카드사 불일치를 못 잡습니다.

개발자를 위한 메모: 검사 순서를 고정하라

검사를 어떤 순서로 두는지에 따라 사용자에게 보이는 오류가 달라집니다. 권장 순서는 입력 정리, 빈 값 확인, 자릿수 확인, 문자 종류 확인, 카드사 확인, 마지막으로 검산입니다. 가장 판단이 쉬운 것부터 걸러야 오류 메시지가 구체적으로 나옵니다.

또 하나 기억할 것은 검산이 통과해도 결제 가능 여부를 뜻하지 않는다는 점입니다. 검증 함수 이름에 유효하다는 단어를 붙이면 몇 달 뒤의 동료가 이 검사를 승인 대신으로 오해합니다. 카드 번호 형식과 함께 읽으면 자릿수와 시작 숫자 규칙까지 정리됩니다.

다음 단계

직접 만든 번호로 계산을 확인하고 싶다면 생성기에서 몇 장을 뽑아 위 순서대로 손으로 더해 보세요. 계산이 맞아떨어지는 것을 확인한 다음에는 코드로 카드 번호 검증하기에서 검증 단계를 어떤 순서로 배치할지 정리해 보는 것이 좋습니다. 생성기에서 나오는 번호는 구조적으로는 유효하지만 실제로 발급된 적이 없으므로 결제에 사용할 수 없습니다.

이어 읽기

테스트용 신용카드 번호 생성기 관련 글