코딩마을방범대

Java에서 QR 코드 만들기 본문

💡 백엔드/Java

Java에서 QR 코드 만들기

신짱구 5세 2023. 7. 27. 09:55
728x90

 

 

QR 이란?

  • 흑백 격자무늬 패턴으로 정보를 나타내는 매트릭스 형식의 이차원 바코드를 의미
  • QR코드는 주로 한국,일본,중국,영국,미국 등에서 많이 사용되며,
    명칭은 덴소 웨이브의 등록 상표 'Quick Response' 에서 유래됨

 

특징 

  • 종래에 많이 쓰이던 바코드의 용량 제한을 극복하고 그 형식과 내용을 확장한 2차원의 바코드로
    종횡의 정보를 가져서 숫자 외에 문자의 데이터를 저장할 수 있음
  • 숫자 최대 7089자, 영문자와 숫자 최대 4296자, 이진 8비트 최대 2953바이트, 한자 1817자를 담을 수 있음

 

 


 

QR과 바코드의 차이점

  QR 바코드
장점
  • 종횡(2차원)으로 사진, 동영상, 지도, 명함 등 다양한 정보를 편리하게 담아낼 수 있음
  • 인식속도, 인식률이 높음
  • 단방향(1차원)으로 숫자나 문자 정보 저장 가능
  • 바코드 일부분이 훼손되어도 인식 가능
단점
  • QR코드 수정, 삭제 불가능
  • 스캐너로 정확하게 가로 방향으로 찍어야 인식 가능
  • 최대 20자까지의 정보만 저장 가능

 

 


 

 

 

 

 

 

 

QR코드의 원리

 

 

1. 포지셔닝 감지 마커 (Positioning detection markers)

 

  • 감지 마커를 이용하여 QR 코드를 인식하여 빠른 속도로 스캔 가능

 

  • 스캐너로 QR 코드를 스캔했을 때 주변 사물에서 QR 코드가 어디에 있는지 확인 가능

 

  • QR 코드를 똑바로 스캔하지 않아도 방향 인식 가능

 

 

 

 


 

 

2. 정렬 표시 (Alignment markings)

 

 

 

  • QR 코드가 구부러진 표면 위에 있어도 바르게 읽을 수 있게 해줌

 

  • 더 많은 정보를 QR 코드에 저장할 수록 더 큰 Alignment Pattern이 필요함

 

 

 

 

 


 

 

3. 타이밍 패턴 (Timing pattern)

 

 

 

 

 

  • QR 스캐너는 반복되는 검정색/흰색 패턴을 확인하여
    QR 코드의 데이터 영역이 얼마나 큰지 파악함

 

 

 

 

 


 

 

4. 버전 정보 (Version information)

 

 

 

 

  • 40개의 QR 코드 버전이 존재함

 

  • 해당 마크로 어떤 버전이 사용되고 있는지 확인함

 

 

 

 


 

 

5. 형식 정보 (Format information)

 

 

 

 

 

  • 에러 오차, 데이터 마스크 패턴 정보를 포함하고 있어
    QR 코드를 더 쉽게 스캔할 수 있게 해줌

 

 

 

 

 


 

 

6. 데이터 및 오류 수정 키 (Data and Error correction keys)

 

 

 

 

  • 에러 정정 메커니즘은 QR 코드의 데이터 영역에 모두 포함되어 있음

 

  • QR 코드의 30%가 훼손되어도 에러 정정 블락을 사용하여 스캔 가능

 

 

 


 

 

7. 조용한 지역? (Quiet Zone)

 

 

  • QR 코드를 둘러싸고 있는 영역

 

  • QR 코드를 주변 사물로부터 QR코드의 영역을 확보하게 해줌

 

  • 필수로 필요한 부분임

 

 

 

 

 

 


 

 

 

 

 

 

QR 코드를 생성할 수 있는 방법에는 여러가지가 있지만,

Java에서 GoogleAuthenticator을 이용해 QR 코드를 생성하는 방법을 알아볼 것이다!

 

 


 

 

 

환경 설정

 

 

build.gradle

// https://mvnrepository.com/artifact/com.warrenstrange/googleauth
implementation 'com.warrenstrange:googleauth:1.5.0'

 


 

Configuration

@RequiredArgsConstructor
@Configuration
public class GAuthenticatorConfig {
	// ICredentialRepository를 상속받은 클래스
	private final CredentialBiz credentialBiz;

	@Bean
	public GoogleAuthenticator googleAuthenticator() 
		GoogleAuthenticatorConfigBuilder googleAuthenticatorConfigBuilder = new GoogleAuthenticatorConfigBuilder()
			// 인증코드 유효시간 설정
			.setTimeStepSizeInMillis( TimeUnit.SECONDS.toMillis( 60 ) )
			// window size를 유지할 시간
			.setWindowSize(3)
			// verification code 자릿수 설정
			.setCodeDigits(6))
			// 스크래치 코드 수 설정
			.setNumberOfScratchCodes(5);
		// 설정한 cofing 대로 GoogleAuthenticator 생성
		GoogleAuthenticator gAuth = new GoogleAuthenticator(googleAuthenticatorConfigBuilder.build());

		gAuth.setCredentialRepository(credentialBiz);

		return gAuth;
	}
}

 

 

 

 


 

 

 

 

 

ICredentialRepository를 상속받는 비즈니스 로직 클래스

 

@RequiredArgsConstructor
@Component
public class CredentialBiz implements ICredentialRepository {
    private final CredentialRepository credentialRepository;
    private final CredentialScratchRepository credentialScratchRepository;
    
    @Override
    public void saveUserCredentials(String userKey, String secretKey, int validationCode, List<Integer> scratchCodes) {
    }
    
    @Override
    public String getSecretKey(String userKey) {
    }
}

 


 

saveUserCredentials()

GoogleAuth 자격 인증서를 만들기 위해 필요한 GoogleAuthenticator 클래스를 살펴보면,

createCredentials 메소드를 호출할 때 인자를 받지 않는 메소드와 인자로 userName을 받는 메소드 두 개가 존재한다.

 

두 메소드의 역할 차이는

인자가 빈 메소드는 GoogleAuthenticatorKey를 만들어 리턴하고,

userName을 받는 메소드는 인자가 빈 메소드를 호출해 Key값을 받은 후 repository에 저장 후 리턴해준다.

 

여기서 repository가 바로 ICredentialRepository 를 상속받은 클래스다.

Credentials을 저장할 Entity를 만들어 프로젝트에 맞게 저장해주는 로직을 짜면 된다!

@Override
public GoogleAuthenticatorKey createCredentials(){
	// Allocating a buffer sufficiently large to hold the bytes required by
	// the secret key.
	int bufferSize = config.getSecretBits() / 8;
	byte[] buffer = new byte[bufferSize];

	secureRandom.nextBytes(buffer);

	// Extracting the bytes making up the secret key.
	byte[] secretKey = Arrays.copyOf(buffer, bufferSize);
	String generatedKey = calculateSecretKey(secretKey);

	// Generating the verification code at time = 0.
	int validationCode = calculateValidationCode(secretKey);
	
	// Calculate scratch codes
	List<Integer> scratchCodes = calculateScratchCodes();

	return new GoogleAuthenticatorKey
		.Builder(generatedKey)
		.setConfig(config)
		.setVerificationCode(validationCode)
		.setScratchCodes(scratchCodes)
		.build();
}

@Override
public GoogleAuthenticatorKey createCredentials(String userName){
	// Further validation will be performed by the configured provider.
	if (userName == null){
		throw new IllegalArgumentException("User name cannot be null.");
	}

	GoogleAuthenticatorKey key = createCredentials();

	ICredentialRepository repository = getValidCredentialRepository();
	repository.saveUserCredentials(
		userName,
		key.getKey(),
		key.getVerificationCode(),
		key.getScratchCodes());
	return key;
}

 


 

getSecretKey()

위와 비슷하게 repository를 통해 SecretKey를 가져오는 로직을 짜면 된다.

GoogleAuthenticator를 통해 검증할 때 사용한다. ( 아래 코드 참고 )

@Override
public boolean authorizeUser(String userName, int verificationCode, long time){
	ICredentialRepository repository = getValidCredentialRepository();

	return authorize(repository.getSecretKey(userName), verificationCode, time);
}

@Override
public boolean authorize(String secret, int verificationCode, long time){
	// Checking user input and failing if the secret key was not provided.
	if (secret == null){
		throw new IllegalArgumentException("Secret cannot be null.");
	}

	// Checking if the verification code is between the legal bounds.
	if (verificationCode <= 0 || verificationCode >= this.config.getKeyModulus()){
		return false;
	}

	// Checking the validation code using the current UNIX time.
	return checkCode(
		secret,
		verificationCode,
		time,
		this.config.getWindowSize());
}

 

 

 


 

 

 

 

QR URL 생성하기

 

StringBuilder issuer = new StringBuilder();
issuer.append("발급자 정보")
    .append("&변수명=")
    .append("담고싶은 문자열");

GoogleAuthenticatorKey authenticatorKey = googleAuthenticator.createCredentials();
String url = GoogleAuthenticatorQRGenerator.getOtpAuthTotpURL(issuer.toString(), "유니크한 아이디", authenticatorKey);

참고사이트

메소드 인자 설명
getOtpAuthTotpURL String issuer,
String accountName,
GoogleAuthenticatorKey credentials
이메일, QR 코드 또는 기타 방법을 통해 사용자에게 전송될 수 있습니다. 
이 URI에는 비밀이 포함되어 있으므로 보안 전송을 사용하십시오.
getOtpAuthURL Google Authenticator 애플리케이션에 로드할 QR 바코드를 생성하기 위해 Google Chart API 호출의 URL을 반환합니다. 
사용자는 스마트폰의 애플리케이션으로 이 바코드를 스캔하거나 수동으로 암호를 입력합니다.

 

인증서를 만들 때 인수로 userName 을 주면 위에서 설정한 바와 같이 repository에 저장된다.

 

 

 


 

 

이후 서버에서 생성한 URL을 기반으로 프론트 화면에 표시해주면 된다.

 

리액트를 사용할 경우 qrcode.react 패키지를 설치해주면 된다.

아래 같은 방식으로 value에 URL을 넣어주면 된다.

<QRCode className='qr'
	value={qrUrl}
	size={128}
/>

 

 


 

 

 

 

 

 

QR 검증

 

QR 검증을 위해선 모바일에서 Google Auth 앱을 설치해줘야한다고 한다.

※ 앱 개발 시에 Google 서비스에 등록하면 Google Auth 앱 의존성을 추가할 수 있다.

 

Google Auth 앱에서 생성된 QR 코드를 인식하여 생성되는 code 값을 통해 검증할 수 있다.

 

혹은 

 

repository를 통해 저장된 code값으로 검증해도 된다!

 

 

 

 

 

 

 

 


참고사이트

QR코드 발명 이유와 원리, 장점, 단점, 바코드 비교

Google QR Code 사용해보기 - 1

 

 

728x90