Computer >> 컴퓨터 >  >> 프로그래밍 >> Java

자바(Java)로 신용카드 번호 유효성 검사하기 — Luhn 알고리즘 완벽 가이드

신용카드 번호 유효성 검사란?

신용카드 번호를 나타내는 긴 숫자가 주어졌을 때, 해당 번호가 실제로 유효한 카드 번호인지 프로그램으로 판별하는 것이 이 글의 목표입니다. 여기서는 널리 사용되는 Luhn 알고리즘(또는 mod 10 검사)을 활용한 자바 구현 방법을 단계별로 살펴보겠습니다.

유효한 신용카드 번호의 기본 조건

신용카드 번호가 유효하다고 판정하기 전에 반드시 확인해야 할 조건은 다음과 같습니다.

먼저, 카드 번호는 13자리에서 16자리 사이여야 하며, 카드 종류에 따라 정해진 숫자로 시작해야 합니다.

  • 모든 Visa 카드는 4로 시작합니다.
  • 모든 MasterCard5로 시작합니다.
  • American Express 카드는 37로 시작합니다.
  • 모든 Discover 카드는 6으로 시작합니다.

카드 번호 유효성 검사 절차 (Luhn 알고리즘)

  1. 1단계 — 오른쪽에서 왼쪽 방향으로 각 자릿수에 2를 곱합니다. 곱한 결과가 한 자리 수라면 그대로 두고, 두 자리 수라면 각 자릿수를 더해 한 자리 수로 만듭니다. (예: 12 → 1+2 = 3)
  2. 2단계 — 카드 번호를 오른쪽에서 왼쪽으로 읽으면서 홀수 위치에 있는 자릿수들을 모두 더합니다.
  3. 3단계 — 1단계에서 얻은 한 자리 수들을 모두 더합니다.
  4. 4단계 — 2단계와 3단계의 결과를 서로 더합니다.
  5. 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차 검증 도구로 활용하는 것이 좋습니다.