gimmickbutreal

[Springboot/Java] QR 코드 생성/저장/출력 본문

Programming/Spring Boot

[Springboot/Java] QR 코드 생성/저장/출력

isshosng 2023. 3. 12. 11:16

Java, Oracle, Spring boot 환경에서 백엔드로 구축

 

1. Mapper 설계

1
2
3
4
5
6
7
8
9
@Mapper
public interface QrCodeMapper {
    /**
     * 아래의 매개변수를 DB에 QR코드 정보로 삽입하는 역할
     * @param link
     * @param qrCode
     */
    void insertQrCode(@Param("link"String link, @Param("qrCode"byte[] qrCode);
}
cs

- 위 코드는 Mybatis에서 사용하는 Mapper 인터페이스

- @Mapper : 해당 애너테이션은 MyBatis가 인터페이스를 Mapper로 인식하도록 함

- @Param : 해당 애너테이션은 메서드 배개변수 이름과 SQL 매개변수 이름을 매핑시켜줌. 이를 사용해 insertQrCode 메서드에서 매개변수로 받은 link와 qrCode를 Oracle에서 사용할 수 있음

 

 

2. Service 설계

1
2
3
4
public interface QrCodeService {
    public byte[] generateQrCode(String link) throws IOException, WriterException;
 
}
cs

- link 매개변수를 받아 생성된 QR 코드를 byte 배열로 반환

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
@RequiredArgsConstructor
public class QrCodeServiceImpl implements QrCodeService {
    private final QrCodeMapper qrCodeMapper; // QrCodeMapper 빈 객체 주입을 위한 final 필드
 
    // 매개변수로 받은 문자열 링크를 이용해 QR 코드를 생성하고 DB에 삽입한 후, 생성된 QR 코드 바이트 배열을 반환하는 메서드
    public byte[] generateQrCode(String link) throws IOException, WriterException {
 
      // QR 코드 생성
      byte[] qrCodeBytes = QRCodeGenerator.generateQRCodeImage(link);
 
      // DB에 QR 코드 삽입
      qrCodeMapper.insertQrCode(link, qrCodeBytes);
 
      return qrCodeBytes; // 생성된 QR 코드 바이트 배열을 반환
    }
}
cs

- @Service : 해당 애너테이션은 해당 클래스를 스프링의 Bean으로 등록함. Component Scan 기능을 이용해 스프링 컨테이너가 자동으로 생성

- @RequiredArgsConstructor : 해당 애너테이션은 생성자 주입을 위한 Lombok 애너테이션으로, 필드 주입 대신 생성자를 이용해 의존성 주입을 할 수 있음

- 생성자를 통해 의존성을 주입하는 방식을 사용하면, 코드의 결합도가 낮아져 유지보수가 용이해지며, 객체 생성 시점에 모든 의존성이 주입되어 NullPointerException 등의 오류를 방지할 수 있음

- Impl 클래스를 따로 만드는 이유

1. 인터페이스와 구현체를 분리함으로써 유연성을 높이기 위함

 - 인터페이스는 메서드의 명세만을 정의하고, 구현체에서는 이를 구체적으로 구현하게 됨. 이를 통해, 다양한 구현체를 만들 수 있으며, 런타임에 구현체를 동적으로 변경할 수 있음

2. 코드의 일관성과 가독성 향상

 - 인터페이스에 정의된 메서드의 이름과 매개변수, 반환값 등을 따르기 때문

 

 

3. Repository 설계

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Repository
public class QrCodeRepository {
 
    private final QrCodeMapper qrCodeMapper; // 데이터베이스와 상호작용을 위해 생성자에서 해당 객체를 받아와 필드에 할당함
 
 
    public QrCodeRepository(QrCodeMapper qrCodeMapper) {
        this.qrCodeMapper = qrCodeMapper;
    }
 
    public void saveQrCode(String link, Long id) throws IOException, WriterException {
        link = "https://www.naver.com";
        id = 12L;
 
        // QR 코드 생성
        byte[] qrCodeBytes = QRCodeGenerator.generateQRCodeImage(link);
 
        // 데이터베이스에 QR 코드 삽입
        qrCodeMapper.insertQrCode(link, qrCodeBytes);
    }
}
cs

- link와 id 값은 임의로 지정한 것이며, 메서드 파라미터로 전달 받아서 사용하려면 임의로 설정한 두 값을 지우고 insertQrCoded 메서드에 id 값을 추가로 전달받도록 해야함. QrCodeMapper에도 Long id 변수를 추가.

 

 

4. Generator 설계

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class QRCodeGenerator {
 
    /**
     * 주어진 링크를 인코딩하여 QR 코드 이미지를 생성하고,
     * 그 이미지를 byte 배열 형태로 반환하는 메서드
     * @param link
     * @return QR 코드 이미지를 바이트 배열 형태로 변환
     * @throws WriterException
     * @throws IOException
     */
    public static byte[] generateQRCodeImage(String link) throws WriterException, IOException {
 
        // QR코드 생성 옵션 설정
        Map<EncodeHintType, Object> hintMap = new HashMap<>();
        hintMap.put(EncodeHintType.MARGIN, 0);
        hintMap.put(EncodeHintType.CHARACTER_SET,"UTF-8");
 
        // QR 코드 생성
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.encode(link, BarcodeFormat.QR_CODE, 200200, hintMap);
 
        // QR 코드 이미지 생성
        BufferedImage qrCodeImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
 
        // QR 코드 이미지를 바이트 배열로 변환, byteArrayOutputStream에 저장
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ImageIO.write(qrCodeImage,"png", byteArrayOutputStream);
        byteArrayOutputStream.flush();
        byte[] qrCodeBytes = byteArrayOutputStream.toByteArray();
        byteArrayOutputStream.close();
 
        return qrCodeBytes;
    }
}
cs

 

5. Controller 설계

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Controller
// 해당 클래스의 생성자를 자동으로 생성
// QrCodeController 생성자는 QrCodeService 타입의 매개변수를 받으며, @Autowired 어노테이션을 사용하지 않아도 자동으로 의존성 주입이 가능
@RequiredArgsConstructor
public class QrCodeController {
 
    // QrCodeService 클래스에 대한 객체 생성
    private final QrCodeService qrCodeService;
 
    /** HTTP GET 요청이 /qr 경로로 들어왔을 때 showQrCode 메서드 실행
     *
     * @param model
     * @return qr/qrcode view
     * @throws IOException
     * @throws WriterException
     */
 
    @GetMapping("/qr")
    public String showQrCode(Model model) throws IOException, WriterException {
 
        // QR 코드 생성 및 모델에 추가
        String link = "https://www.naver.com";
        // 변수에 생성된 QR 코드 이미지 데이터가 저장
        byte[] qrCodeBytes = qrCodeService.generateQrCode(link);
        // byte 배열 형태로 저장된 QR 코드 이미지 데이터를 Base64 인코딩하여 문자열 형태로 변환
        String qrCode = Base64.getEncoder().encodeToString(qrCodeBytes);
        // qrCode 변숫값을 qrCode라는 이름으로 Model 객체에 추가. Model 객체에 데이터를 추가하면 Spring MVC는 해당 데이터를 View로 전달하여 화면에 렌더링
        model.addAttribute("qrCode", qrCode);
 
        return "qr/qrcode";
    }
}
cs

 

6. View 설계

 

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>QR Code</title>
</head>
<body>
<h1>QR Code</h1>
<img th:src="'data:image/png;base64,'+${qrCode}" alt="QR Code">
</body>
</html>
 
cs

 

7. 출력 화면

 

 

직접 개발한 코드며 리팩토링을 진행할 예정.

백엔드가 아닌 클라이언트 상에서 개발하는 방법도 매우 좋은 선택