💡My project/다모아 : 개발자 중개 플랫폼
[다모아 : 기능 분석] 전자 서명 작성, 저장 및 조회 기능
Song hyun
2024. 10. 21. 16:15
728x90
반응형
[다모아] 전자 서명 작성, 저장 및 조회 기능
오늘은 전자 서명 작성 및 등록 기능에 대해 설명해보려고 한다. 구현한 페이지/참고한 페이지, 기능은 아래와 같다.
참고한 서비스들은 더 많지만... 핵심 기능이 아닌데다, 파고들어가자면 끝이 없어서.. 가장 필수적인 부분만 구현해봤다.
- 간단하게 유저가 사인을 마우스로 그리면,
- 해당 사인을 이미지 형식으로 서버에 저장하고,
- 서버 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
반응형