신용카드 번호 유효성 검사란?
신용카드 번호를 나타내는 긴 숫자가 주어졌을 때, 해당 번호가 실제로 유효한 카드 번호인지 프로그램으로 판별하는 것이 이 글의 목표입니다. 여기서는 널리 사용되는 Luhn 알고리즘(또는 mod 10 검사)을 활용한 자바 구현 방법을 단계별로 살펴보겠습니다.
유효한 신용카드 번호의 기본 조건
신용카드 번호가 유효하다고 판정하기 전에 반드시 확인해야 할 조건은 다음과 같습니다.
먼저, 카드 번호는 13자리에서 16자리 사이여야 하며, 카드 종류에 따라 정해진 숫자로 시작해야 합니다.
- 모든 Visa 카드는 4로 시작합니다.
- 모든 MasterCard는 5로 시작합니다.
- American Express 카드는 37로 시작합니다.
- 모든 Discover 카드는 6으로 시작합니다.
카드 번호 유효성 검사 절차 (Luhn 알고리즘)
- 1단계 — 오른쪽에서 왼쪽 방향으로 각 자릿수에 2를 곱합니다. 곱한 결과가 한 자리 수라면 그대로 두고, 두 자리 수라면 각 자릿수를 더해 한 자리 수로 만듭니다. (예: 12 → 1+2 = 3)
- 2단계 — 카드 번호를 오른쪽에서 왼쪽으로 읽으면서 홀수 위치에 있는 자릿수들을 모두 더합니다.
- 3단계 — 1단계에서 얻은 한 자리 수들을 모두 더합니다.
- 4단계 — 2단계와 3단계의 결과를 서로 더합니다.
- 5단계 — 최종 결과가 10으로 나누어 떨어지면 카드 번호는 유효하고, 그렇지 않으면 유효하지 않습니다.
예제 입력 및 출력
입력: n = 4440967484181607 출력: 4440967484181607 is valid 입력: n = 379354508162306 출력: 379354508162306 is valid
문제 해결 접근 방식
숫자 4440967484181607에 대해 Luhn 검사(mod 10 검사)를 적용하여 유효성을 판별합니다. 카드 길이·접두사(prefix)·체크섬 세 가지 조건을 모두 통과해야만 유효한 번호로 인정됩니다.
알고리즘
시작
Step 1 -> void main(String[] args) 함수에서
cnumber = 4440967484181607L 로 선언 및 초기화
validitychk 함수 호출
결과 출력
Step 2 -> boolean validitychk(long cnumber) 함수에서
thesize(cnumber) >= 13 && thesize(cnumber) <= 16 &&
(prefixmatch(cnumber, 4) || prefixmatch(cnumber, 5) ||
prefixmatch(cnumber, 37) || prefixmatch(cnumber, 6)) &&
((sumdoubleeven(cnumber) + sumodd(cnumber)) % 10 == 0) 를 반환
Step 3 -> int sumdoubleeven(long cnumber) 함수에서
sum = 0, num = cnumber + "" 로 초기화
i = thesize(cnumber) - 2 부터 i >= 0 까지 i -= 2 반복
sum += getDigit(Integer.parseInt(num.charAt(i) + "") * 2)
sum 반환
Step 4 -> int getDigit(int cnumber) 함수에서
cnumber < 9 이면 cnumber 반환
그 외에는 cnumber / 10 + cnumber % 10 반환
Step 5 -> int sumodd(long cnumber) 함수에서
sum = 0, num = cnumber + "" 로 초기화
i = thesize(cnumber) - 1 부터 i >= 0 까지 i -= 2 반복
sum += Integer.parseInt(num.charAt(i) + "")
sum 반환
Step 6 -> boolean prefixmatch(long cnumber, int d) 함수에서
getprefx(cnumber, thesize(d)) == d 를 반환
Step 7 -> int thesize(long d) 함수에서
num = d + "" 로 설정 후 num.length() 반환
Step 8 -> long getprefx(long cnumber, int k) 함수에서
thesize(cnumber) > k 이면
num = cnumber + "" 설정 후 Long.parseLong(num.substring(0, k)) 반환
그 외에는 cnumber 반환
종료
전체 자바 소스 코드
import java.util.Scanner;
public class Main {
// 메인 메서드
public static void main(String[] args) {
long cnumber = 4440967484181607L;
System.out.println(cnumber + " is " + (validitychk(cnumber) ? "valid" : "invalid"));
}
// 카드 번호가 유효하면 true 반환
public static boolean validitychk(long cnumber) {
return (thesize(cnumber) >= 13 && thesize(cnumber) <= 16) && (prefixmatch(cnumber, 4)
|| prefixmatch(cnumber, 5) || prefixmatch(cnumber, 37) || prefixmatch(cnumber, 6))
&& ((sumdoubleeven(cnumber) + sumodd(cnumber)) % 10 == 0);
}
// 2단계의 결과를 반환
public static int sumdoubleeven(long cnumber) {
int sum = 0;
String num = cnumber + "";
for (int i = thesize(cnumber) - 2; i >= 0; i -= 2)
sum += getDigit(Integer.parseInt(num.charAt(i) + "") * 2);
return sum;
}
// 한 자리 수면 그대로 반환하고,
// 그렇지 않으면 두 자릿수의 합을 반환
public static int getDigit(int cnumber) {
if (cnumber < 9)
return cnumber;
return cnumber / 10 + cnumber % 10;
}
// 카드 번호에서 홀수 위치 자릿수의 합을 반환
public static int sumodd(long cnumber) {
int sum = 0;
String num = cnumber + "";
for (int i = thesize(cnumber) - 1; i >= 0; i -= 2)
sum += Integer.parseInt(num.charAt(i) + "");
return sum;
}
// 숫자 d가 cnumber의 접두사이면 true 반환
public static boolean prefixmatch(long cnumber, int d) {
return getprefx(cnumber, thesize(d)) == d;
}
// 숫자 d의 자릿수 개수를 반환
public static int thesize(long d) {
String num = d + "";
return num.length();
}
// 숫자의 앞 k개 자릿수를 반환.
// 숫자의 자릿수가 k보다 작으면 원래 숫자를 반환.
public static long getprefx(long cnumber, int k) {
if (thesize(cnumber) > k) {
String num = cnumber + "";
return Long.parseLong(num.substring(0, k));
}
return cnumber;
}
}
실행 결과
4440967484181607 is valid
마무리
이처럼 Luhn 알고리즘을 활용하면 별도의 외부 API 없이도 순수 자바 코드만으로 신용카드 번호의 형식적 유효성을 빠르게 검증할 수 있습니다. 다만 이 검사는 번호의 형식적 타당성만 확인할 뿐 실제 카드의 존재 여부나 잔액 등을 보장하지는 않으므로, 결제 시스템에서는 1차 검증 도구로 활용하는 것이 좋습니다.