💡My project/다모아 : 개발자 중개 플랫폼

[다모아 : 기능 분석] 전자 서명 작성, 저장 및 조회 기능

Song hyun 2024. 10. 21. 16:15
728x90
반응형

[다모아] 전자 서명 작성, 저장 및 조회 기능

 

 

 

 오늘은 전자 서명 작성 및 등록 기능에 대해 설명해보려고 한다. 구현한 페이지/참고한 페이지, 기능은 아래와 같다. 

 

전자 서명 기능 구현 페이지
참고한 위시켓 - 전자 서명 기능

 

 

참고한 서비스들은 더 많지만... 핵심 기능이 아닌데다, 파고들어가자면 끝이 없어서.. 가장 필수적인 부분만 구현해봤다.

  1. 간단하게 유저가 사인을 마우스로 그리면,
  2. 해당 사인을 이미지 형식으로 서버에 저장하고,
  3. 서버 DB에 유저-사인 데이터를 저장하는 식으로 구현했다.

 

 

1. make_sign.mustache

(1) HTML

{{>layout/header}}

<style type="text/css">
canvas {
  background: #fff;
  border: 1px dashed;
}
</style>


<h1>날인 등록하기</h1>
<p>마우스 드래그로 그림을 그려보세요</p>

<label for="name">사인 이름:</label>
<input type="text" id="name" placeholder="이름을 입력하세요" />

<button id="saveButton">Save</button>
<button onclick="clearCanvas()">Clear Canvas</button>
<p></p>
<canvas width="150" height="100"></canvas>
{{>layout/footer}}

 

(2) JavaScript


<script>
    // 캔버스 및 2D 컨텍스트 설정
    const canvas = document.querySelector('canvas');
    const c = canvas.getContext('2d');
    c.fillStyle = 'black';

    // 드로잉 상태 및 마지막 위치 변수
    let isDrawing = false;
    let lastX = 0;
    let lastY = 0;

    // 점 그리기 함수
    function draw(x, y) {
        c.beginPath();
        c.arc(x, y, 0.5, 0, Math.PI * 2); // 원의 반지름을 0.5로 설정
        c.closePath();
        c.fill();
    }

    // 마우스 클릭 시작
    canvas.addEventListener('mousedown', (event) => {
        isDrawing = true;
        lastX = event.offsetX;
        lastY = event.offsetY;
    });

    // 마우스 이동 이벤트
    canvas.addEventListener('mousemove', (event) => {
        if (isDrawing) {
            const currentX = event.offsetX;
            const currentY = event.offsetY;

            // 현재 위치와 마지막 위치 사이의 거리 계산
            const distance = Math.hypot(currentX - lastX, currentY - lastY);
            const steps = Math.ceil(distance / 1); // 1픽셀마다 점을 찍도록 설정

            // 선 그리기
            for (let i = 0; i < steps; i++) {
                const x = lastX + (currentX - lastX) * (i / steps);
                const y = lastY + (currentY - lastY) * (i / steps);
                draw(x, y);
            }

            // 마지막 점 업데이트
            lastX = currentX;
            lastY = currentY;
        }
    });

    // 마우스 클릭 종료
    canvas.addEventListener('mouseup', () => {
        isDrawing = false;
    });

    // 캔버스 지우기 함수
    function clearCanvas() {
        c.clearRect(0, 0, canvas.width, canvas.height);
    }
    // 저장 기능
    document.getElementById('saveButton').addEventListener('click', async () => {
        const dataURL = canvas.toDataURL('image/png');
        const name = document.getElementById('name').value; // 사인 이름 가져오기

        const response = await fetch('/sign', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ image: dataURL, name: name }), // 이름 추가
        });

        if (response) {
            alert('이미지가 성공적으로 저장되었습니다.');
        }
    });

</script>

 

2. SignController

(1) 사인 정보 저장 로직

/**
     * 전자 서명 등록 요청
     * @param request
     * @return
     */
    @PostMapping("")
    public String saveRegisterProc(@RequestBody Map<String, String> request){
        String base64Image = request.get("image"); // 이미지 바이너리 데이터 받기
        String name = request.get("name"); // 사인 이름 받기

        // 메타데이터 제거
        String imageData = base64Image.split(",")[1];
        // 비정상적인 문자 제거
        imageData = imageData.replaceAll("[^A-Za-z0-9+/=]", "");

        // 패딩 추가
        while (imageData.length() % 4 != 0) {
            imageData += "=";
        }

        // Base64 문자열을 byte[]로 변환
        byte[] imageBytes = Base64.getDecoder().decode(imageData);
        // 파일 저장 경로
        String filePath = uploadSigndir; // 저장할 경로
        int userId=1; // 세션값 불러올 예정
        
        // 사인 파일명
        String uploadFileName=userId+"_"+ UUID.randomUUID()+".png";

        AddSignDTO newSign = AddSignDTO.builder()
                            .name(name)
                            .fileData(imageBytes)
                            .userId(userId)
                            .uploadFileName(uploadFileName)
                            .build();
        
        // 이미지 파일로 저장
        try (FileOutputStream fos = new FileOutputStream(filePath+ File.separator+uploadFileName)) {
            fos.write(imageBytes);
            signService.addNewSign(newSign);
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("이미지 저장 실패").toString();
        }

        return ResponseEntity.ok("이미지 저장 성공").toString();
    }


(2) 파일명 변경(경로 추가) 메서드

// 사인 경로 설정
    private List<Sign> setSignPath(List<Sign> list){
        for(int a=0; a<list.size(); a++){
            list.get(a).setFileName(list.get(a).setUpSignImage());
        }
        return list;
    }

 

 

3. sign_list.mustache

{{>layout/header}}
<h1>전자 서명 목록</h1>

<div class="sign-board">
    <div class="sign-container">
        {{#signList}}
            <div class="sign-box">
                <p>{{name}}</p>
                <img src="{{fileName}}" alt="서명 이미지">
            </div>
        {{/signList}}
    </div>
    <button onclick="location.href='/sign'">서명 등록하기</button>
</div>
{{>layout/footer}}
728x90
반응형