diff --git a/src/main/java/com/devcard/devcard/auth/config/SecurityConfig.java b/src/main/java/com/devcard/devcard/auth/config/SecurityConfig.java
index b8e6ee3..f9dbbb6 100644
--- a/src/main/java/com/devcard/devcard/auth/config/SecurityConfig.java
+++ b/src/main/java/com/devcard/devcard/auth/config/SecurityConfig.java
@@ -38,7 +38,8 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
"/notice",
"/notice/**",
"/qna",
- "/qna/**"
+ "/qna/**",
+ "/shared/**"
).permitAll();
// 그 외의 모든 요청은 인증 필요
auth.anyRequest().authenticated();
diff --git a/src/main/java/com/devcard/devcard/card/controller/page/CardPageController.java b/src/main/java/com/devcard/devcard/card/controller/page/CardPageController.java
index 25c5553..3bb95f6 100644
--- a/src/main/java/com/devcard/devcard/card/controller/page/CardPageController.java
+++ b/src/main/java/com/devcard/devcard/card/controller/page/CardPageController.java
@@ -78,4 +78,11 @@ public String editCardView(@PathVariable("cardId") Long cardId, Model model) {
return "card-edit";
}
+ @GetMapping("/shared/cards/{cardId}")
+ public String sharedCardView(@PathVariable("cardId") Long cardId, Model model) {
+ CardResponseDto card = cardService.getCard(cardId);
+ model.addAttribute("card", card);
+ return "card-shared-view";
+ }
+
}
diff --git a/src/main/java/com/devcard/devcard/card/controller/rest/QrController.java b/src/main/java/com/devcard/devcard/card/controller/rest/QrController.java
index a73e2a2..39b7466 100644
--- a/src/main/java/com/devcard/devcard/card/controller/rest/QrController.java
+++ b/src/main/java/com/devcard/devcard/card/controller/rest/QrController.java
@@ -1,31 +1,35 @@
-package com.devcard.devcard.card.controller.rest;
-
-import com.devcard.devcard.card.dto.QrResponseDto;
-import com.devcard.devcard.card.service.QrServiceImpl;
-import com.google.zxing.WriterException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.io.IOException;
-
-@RestController
-public class QrController {
-
- private final QrServiceImpl qrServiceImpl;
-
- @Autowired
- public QrController(QrServiceImpl qrServiceImpl) {
- this.qrServiceImpl = qrServiceImpl;
- }
-
- @GetMapping("/cards/{card_id}/qrcode")
- public ResponseEntity createQR(@PathVariable (name = "card_id") Long cardId) throws IOException, WriterException {
- String qrUrl = qrServiceImpl.createQr(cardId);
-
- return ResponseEntity.ok()
- .body(new QrResponseDto(qrUrl));
- }
-}
+package com.devcard.devcard.card.controller.rest;
+
+import com.devcard.devcard.card.service.QrServiceImpl;
+import com.google.zxing.WriterException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+@RestController
+public class QrController {
+
+ private final QrServiceImpl qrServiceImpl;
+
+ @Autowired
+ public QrController(QrServiceImpl qrServiceImpl) {
+ this.qrServiceImpl = qrServiceImpl;
+ }
+
+ @GetMapping("/cards/{card_id}/qrcode-image")
+ public ResponseEntity generateQrImage(@PathVariable(name = "card_id") Long cardId) throws IOException, WriterException {
+ ByteArrayOutputStream qrImageStream = qrServiceImpl.generateQrImageStream(cardId);
+
+ return ResponseEntity.ok()
+ .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"qrcode.png\"")
+ .contentType(MediaType.IMAGE_PNG)
+ .body(qrImageStream.toByteArray());
+ }
+}
diff --git a/src/main/java/com/devcard/devcard/card/dto/QrResponseDto.java b/src/main/java/com/devcard/devcard/card/dto/QrResponseDto.java
deleted file mode 100644
index 7da8c17..0000000
--- a/src/main/java/com/devcard/devcard/card/dto/QrResponseDto.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.devcard.devcard.card.dto;
-
-public record QrResponseDto(
- String qrcode_url
-) {
-}
diff --git a/src/main/java/com/devcard/devcard/card/service/QrServiceImpl.java b/src/main/java/com/devcard/devcard/card/service/QrServiceImpl.java
index f37e000..7e453ac 100644
--- a/src/main/java/com/devcard/devcard/card/service/QrServiceImpl.java
+++ b/src/main/java/com/devcard/devcard/card/service/QrServiceImpl.java
@@ -1,81 +1,36 @@
-package com.devcard.devcard.card.service;
-
-import com.google.zxing.BarcodeFormat;
-import com.google.zxing.MultiFormatWriter;
-import com.google.zxing.WriterException;
-import com.google.zxing.client.j2se.MatrixToImageWriter;
-import com.google.zxing.common.BitMatrix;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-@Service
-public class QrServiceImpl implements QrService{
-
- private static final int QR_SIZE_WIDTH = 200;
- private static final int QR_SIZE_HEIGHT = 200;
-
- @Value("${qr.domain.uri}")
- private String domainUri;
-
- @Value("${qr.code.directory}")
- private String qrCodeDirectory;
-
- /**
- * @param cardId QR로 만들 명함 ID
- * @return QR 코드 IMAGE 파일 이름만 반환
- */
- @Override
- public String createQr(Long cardId) throws WriterException, IOException {
-
- // QR URL - QR 코드 정보 URL
- String url = generateQrUrl(cardId);
-
- // QR Code - BitMatrix: qr code 정보 생성
- BitMatrix bitMatrix = generateQrCode(url);
-
- // Setting QR Image File Name, Path
- String qrFileName = generateQrFileName(cardId);
- Path qrPath = generateQrFilePath(qrFileName);
-
- // Save QR
- saveQrCodeImage(bitMatrix, qrPath);
-
- return domainUri + "qrcodes/" + qrFileName;
- }
-
- private String generateQrUrl(Long cardId) {
- return domainUri + "cards/" + cardId + "/view";
- }
-
- private BitMatrix generateQrCode(String url) throws WriterException {
- try {
- return new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, QR_SIZE_WIDTH, QR_SIZE_HEIGHT);
- } catch (WriterException e) {
- throw e;
- }
- }
-
- private String generateQrFileName(Long cardId) {
- return "card_id_" + cardId + ".png";
- }
-
- private Path generateQrFilePath(String qrFileName) {
- return Paths.get(qrCodeDirectory + qrFileName);
- }
-
- private void saveQrCodeImage(BitMatrix bitMatrix, Path qrPath) throws IOException {
- try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
- MatrixToImageWriter.writeToStream(bitMatrix, "png", out);
- Files.createDirectories(qrPath.getParent());
- Files.write(qrPath, out.toByteArray());
- } catch (IOException e) {
- throw e;
- }
- }
-}
+package com.devcard.devcard.card.service;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.WriterException;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+@Service
+public class QrServiceImpl {
+
+ private static final int QR_SIZE_WIDTH = 200;
+ private static final int QR_SIZE_HEIGHT = 200;
+
+ @Value("${qr.domain.uri}")
+ private String domainUri;
+
+ public ByteArrayOutputStream generateQrImageStream(Long cardId) throws WriterException, IOException {
+ // QR URL 생성
+ String url = domainUri + "shared/cards/" + cardId;
+
+ // QR Code 생성
+ BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, QR_SIZE_WIDTH, QR_SIZE_HEIGHT);
+
+ // QR Code 이미지를 메모리 스트림에 저장
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream);
+
+ return outputStream;
+ }
+}
diff --git a/src/main/resources/static/css/card/card-detail.css b/src/main/resources/static/css/card/card-detail.css
index b7fc4e2..4338987 100644
--- a/src/main/resources/static/css/card/card-detail.css
+++ b/src/main/resources/static/css/card/card-detail.css
@@ -187,4 +187,12 @@
.delete-button.button:hover {
background-color: #16a085; /* Darker red */
-}
\ No newline at end of file
+}
+
+#qr-image-container {
+ display: none;
+ text-align: center;
+ margin-top: 10px; /* 기존 20px에서 10px로 줄임 */
+ position: relative; /* 컨테이너의 위치를 세부 조정할 수 있도록 설정 */
+ top: -10px; /* 컨테이너를 위로 이동 */
+}
diff --git a/src/main/resources/static/js/card/card-share.js b/src/main/resources/static/js/card/card-share.js
index c32d0d0..8487b32 100644
--- a/src/main/resources/static/js/card/card-share.js
+++ b/src/main/resources/static/js/card/card-share.js
@@ -1,3 +1,4 @@
+// Kakao 공유 버튼
document.getElementById('kakao-share-btn').addEventListener('click', function () {
if (!cardId) {
console.error('Card ID가 제공되지 않았습니다.');
@@ -5,17 +6,16 @@ document.getElementById('kakao-share-btn').addEventListener('click', function ()
return;
}
- // 기본 content 설정
+ // Kakao 공유 설정
const content = {
title: `${cardName}님의 명함`,
imageUrl: 'https://developers.kakao.com/assets/img/about/logos/kakaolink/kakaolink_btn_medium.png',
link: {
- mobileWebUrl: `http://3.34.144.148:8080/cards/${cardId}/view`,
- webUrl: `http://3.34.144.148:8080/cards/${cardId}/view`
+ mobileWebUrl: `http://3.34.144.148:8080/shared/cards/${cardId}`,
+ webUrl: `http://3.34.144.148:8080/shared/cards/${cardId}`
}
};
- // description이 있는 경우에만 추가
if (cardCompany || cardPosition) {
content.description = `회사: ${cardCompany || ''}${cardCompany && cardPosition ? ', ' : ''}직책: ${cardPosition || ''}`;
}
@@ -27,20 +27,21 @@ document.getElementById('kakao-share-btn').addEventListener('click', function ()
{
title: '명함 보기',
link: {
- mobileWebUrl: `http://3.34.144.148:8080/cards/${cardId}/view`,
- webUrl: `http://3.34.144.148:8080/cards/${cardId}/view`
+ mobileWebUrl: `http://3.34.144.148:8080/shared/cards/${cardId}`,
+ webUrl: `http://3.34.144.148:8080/shared/cards/${cardId}`
}
}
],
- fail: function(error) {
+ fail: function (error) {
console.error(error);
handleError('카카오톡 공유에 실패했습니다.', 300);
}
});
});
+// QR 생성 및 렌더링
document.getElementById('qr-share-btn').addEventListener('click', function () {
- const cardId = this.getAttribute('data-card-id'); // QR 버튼에서 cardId 가져오기
+ const cardId = this.getAttribute('data-card-id'); // 버튼에서 cardId 가져오기
if (!cardId) {
console.error('Card ID가 제공되지 않았습니다.');
@@ -48,32 +49,27 @@ document.getElementById('qr-share-btn').addEventListener('click', function () {
return;
}
- fetch(`/cards/${cardId}/qrcode`)
+ const qrContainer = document.getElementById('qr-image-container');
+ qrContainer.innerHTML = ''; // 이전 QR 코드 제거
+
+ // QR 코드 API 요청
+ fetch(`/cards/${cardId}/qrcode-image?t=${new Date().getTime()}`)
.then(response => {
- if (!response.ok) {
- console.error(`서버 응답 오류: ${response.status} ${response.statusText}`);
- return response.text();
- }
- return response.json();
+ if (!response.ok) throw new Error('QR 코드 생성에 실패했습니다.');
+ return response.blob();
})
- .then(data => {
- console.log(data)
- const qrUrl = `${data.qrcode_url}?t=${new Date().getTime()}`; // 타임스탬프 추가
+ .then(blob => {
const qrImage = document.createElement('img');
- qrImage.src = qrUrl;
+ qrImage.src = URL.createObjectURL(blob);
qrImage.alt = 'QR Code';
qrImage.style.width = '200px';
- qrImage.style.height = 'auto';
-
- const qrContainer = document.getElementById('qr-image-container');
- qrContainer.innerHTML = ''; // 기존 내용 지우기
- qrContainer.appendChild(qrImage); // QR 코드 이미지 추가
+ qrImage.style.height = '200px';
- qrContainer.style.display = 'block';
- handleSuccess('QR 코드가 성공적으로 생성되었습니다.', 400);
+ qrContainer.appendChild(qrImage); // QR 이미지 추가
+ qrContainer.style.display = 'block'; // QR 컨테이너 표시
})
.catch(error => {
- console.error('QR 코드 fetch 오류:', error);
+ console.error('QR 코드 생성 중 오류:', error);
handleError('QR 코드 생성에 실패했습니다.', 400);
});
});
diff --git a/src/main/resources/templates/card-detail.html b/src/main/resources/templates/card-detail.html
index 6e6fb92..4f784e6 100644
--- a/src/main/resources/templates/card-detail.html
+++ b/src/main/resources/templates/card-detail.html
@@ -100,6 +100,8 @@ 그룹 선택
const cardCompany = /*[[${card.company}]]*/ ''; // 회사 이름
const cardPosition = /*[[${card.position}]]*/ ''; // 직책
+
+
+
+
+
[[${card.name}]] 명함
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/templates/card-shared-view.html b/src/main/resources/templates/card-shared-view.html
new file mode 100644
index 0000000..e962136
--- /dev/null
+++ b/src/main/resources/templates/card-shared-view.html
@@ -0,0 +1,114 @@
+
+
+