안녕하세요. 인텔리원스튜디오(IntelliOneStudio)입니다.
오늘은 ChatGPT를 활용하여 업로드 파일 암호화하고 서버에서 복호화 하는 기능을 만들어볼께요.
클라이언트가 HTTPS도 불안해했어요
HTTPS로 암호화 처리가 되면 전송 구간에서 문제가 생기는 건 극히 드문 일이긴 해요.
그럼에도 클라이언트가 추가적인 암호화 기능을 요구했어요.
[클라이언트 요구 사항]
- 구현해야할 기능
1. 웹 클라이언트 사용자가 엑셀을 업로드해서 서버로 전송하기 전에 엑셀 파일을 암호화 해야 함
2. HTTPS 구간에서 암호화된 파일이 전송되어야 함
3. 전송되어진 암호화 파일을 서버에서 복호화 해서 안전한 공간에 저장해야 함
- 개발 기간 : 2일
암호화 처리는 어떻게 해야 할지 몰라요.
일단 ChatGPT와 대화를 시작했어요.
[질문] 안녕 ChatGPT, 웹 클라이언트 사용자가 엑셀을 업로드해서 서버로 보내는데 엑셀 업로드 버튼을 누를 때 엑셀 파일을 암호화 해서 서버로 올리는 기능을 만들고 싶어. 자세한 방법을 알려줄래?
[답변]
엑셀 파일을 웹 클라이언트에서 서버로 업로드할 때 암호화하는 방법에는 여러 단계가 있습니다. 다음은 그 과정을 간단하게 설명한 단계별 가이드입니다:
- 프론트엔드에서의 암호화:
- 사용자가 엑셀 파일을 선택하면, 웹 페이지의 자바스크립트를 사용하여 파일을 읽습니다. 이 때 FileReader API를 사용할 수 있습니다.
- 파일을 읽은 후, 웹 페이지에서 제공하는 암호화 알고리즘(예: AES, RSA 등)을 사용하여 파일의 내용을 암호화합니다. 암호화에는 CryptoJS와 같은 자바스크립트 라이브러리를 사용할 수 있습니다.
- 암호화된 데이터를 서버에 전송하기 위한 FormData 객체에 추가합니다.
- 암호화된 파일의 전송:
- 암호화된 데이터를 담은 FormData 객체를 AJAX 요청을 통해 서버에 전송합니다. 이 때 XMLHttpRequest나 fetch API를 사용할 수 있습니다.
- 서버로 전송하는 동안 HTTPS 프로토콜을 사용하여 데이터 전송 경로를 보안합니다.
- 백엔드에서의 복호화 및 처리:
- 서버 측에서는 암호화된 파일 데이터를 받아 복호화합니다. 이를 위해 프론트엔드와 동일한 암호화 키를 사용해야 합니다.
- 복호화된 데이터를 사용하여 필요한 작업을 수행합니다. 예를 들면, 데이터베이스에 저장하거나 서버 내 다른 프로세스에 전달할 수 있습니다.
- 보안 유지:
- 암호화 키는 안전하게 보관해야 하며, 키 관리에 대한 보안 방침이 필요합니다.
- 서버는 안전한 환경에서 운영되어야 하며, 가능한 모든 보안 조치를 취해야 합니다.
이 과정은 기본적인 개념을 설명하기 위한 것이며, 실제 구현에서는 프론트엔드와 백엔드의 프로그래밍 언어, 사용 가능한 라이브러리, 보안 요구 사항 등에 따라 다를 수 있습니다. 또한, 사용자 인증, 파일 무결성 검사, 암호화 키 관리 등 보안의 다른 측면도 고려해야 합니다.
만약 구체적인 코드 예제나 라이브러리 추천이 필요하시다면, 사용하고 있는 프로그래밍 언어나 프레임워크에 대한 정보를 제공해 주세요. 그래야 더 정확한 가이드를 제공할 수 있습니다.
대략적인 개발 방향이 정해졌어요.
기본적인 코드 개발을 요청했어요.
이제 요구사항에 맞는 코드 개발을 해달라고 했어요.
[질문] 안녕 ChatGPT, 요구 사항에 맞는 코드를 개발해줄래? 서버는 Spring Framework 를 사용하고 있고 개발 언어는 Java로 만들어줘
[답변]
물론입니다. 아래에는 클라이언트 사이드(웹 브라우저)와 서버 사이드(Spring Framework)에서 간단한 파일 암호화 및 업로드 프로세스를 구현하는 예제 코드를 제공합니다.
클라이언트 사이드: HTML과 JavaScript (CryptoJS를 사용한 AES 암호화)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>File Upload with Encryption</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script> </head> <body> <input type="file" id="fileInput" /> <button onclick="uploadFile()">Upload</button> <script> const uploadFile = () => { const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; const reader = new FileReader(); reader.onload = function(event) { const binary = event.target.result; const encrypted = CryptoJS.AES.encrypt(binary, 'secret-key').toString(); const data = new FormData(); data.append('file', new Blob([encrypted]), file.name); fetch('https://yourserver.com/upload', { method: 'POST', body: data }).then(response => { console.log('File uploaded', response); }).catch(error => { console.error('Error:', error); }); }; reader.readAsBinaryString(file); }; </script> </body> </html>
중요: 'secret-key'는 예시를 위해 사용된 것입니다. 실제로는 안전하게 생성하고 관리해야 하는 보안 키를 사용해야 합니다.
서버 사이드: Spring Boot Controller (AES 복호화)
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import org.apache.tomcat.util.codec.binary.Base64; import java.nio.charset.StandardCharsets; @RestController public class FileUploadController { private static final String AES = "AES"; private static final String SECRET_KEY = "secret-key"; @PostMapping("/upload") public String handleFileUpload(@RequestParam("file") MultipartFile file) { try { byte[] encryptedBytes = file.getBytes(); byte[] decryptedBytes = decrypt(encryptedBytes, SECRET_KEY); // Process the decrypted file as needed // ... return "File uploaded and decrypted successfully"; } catch (Exception e) { e.printStackTrace(); return "Error during file upload and decryption"; } } public static byte[] decrypt(byte[] cipherText, String key) throws Exception { SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES); Cipher cipher = Cipher.getInstance(AES); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(Base64.decodeBase64(cipherText)); } }
참고사항:
- 위의 예제는 단순화를 위해 작성되었습니다. 실제로는 암호화 키를 안전하게 관리하고, 예외 처리를 더 철저히 해야 합니다.
- 암호화와 복호화에 사용되는 키는 동일해야 합니다. 또한 이 키는 안전한 방법으로 전달되고 저장되어야 합니다.
- decrypt 메소드는 Base64로 인코딩된 암호화된 데이터를 받고 있으므로 클라이언트 사이드에서도 Base64로 인코딩된 데이터를 보내야 합니다. 위의 클라이언트 사이드 코드는 이를 고려하지 않았습니다. 실제 구현에서는 클라이언트와 서버 사이의 데이터 인코딩 방식을 일치시켜야 합니다.
이 코드 예제들은 암호화 및 파일 업로드의 기본적인 개념을 나타내며, 보안 관련 실제 상황에는 적합하지 않을 수 있습니다. 보안을 위해서는 전문가의 검토가 필요하며, 암호화 키의 관리, 에러 처리, 파일의 유효성 검사 등 여러 보안 측면을 추가로 고려해야 합니다.
서버 쪽만 요청했는데 클라이언트 사이드까지 코드를 만드네요.
정말 놀라워요.
중요: 'secret-key'는 예시를 위해 사용된 것입니다. 실제로는 보안 키 사용?
답변 중에서 아래 내용에 대해 좀 더 설명을 듣고 싶었어요.
중요: 'secret-key'는 예시를 위해 사용된 것입니다. 실제로는 안전하게 생성하고 관리해야 하는 보안 키를 사용해야 합니다.
[질문] 안녕 ChatGPT, 다음의 중요 사항을 구현하는 방법을 알려줄래?
중요: 'secret-key'는 예시를 위해 사용된 것입니다. 실제로는 안전하게 생성하고 관리해야 하는 보안 키를 사용해야 합니다.
[답변]
암호화 키를 안전하게 생성하고 관리하는 것은 보안 프로세스의 핵심 부분입니다. 여기 몇 가지 중요한 단계와 권장 사항이 있습니다:
키 생성:
- 강력한 난수 생성기 사용: 암호화 키는 예측 불가능해야 하며, 강력한 난수 생성기(CSPRNG)를 사용하여 생성해야 합니다.
- 적절한 키 길이 선택: 암호화 알고리즘에 적합한 키 길이를 선택해야 합니다. 예를 들어, AES 암호화의 경우 일반적으로 128, 192, 또는 256 비트 키를 사용합니다.
- 키 생성 라이브러리 활용: Java에서는 KeyGenerator 또는 SecureRandom 클래스를 사용하여 암호화 키를 생성할 수 있습니다.
키 저장:
- 환경 변수: 암호화 키를 코드에 직접 쓰지 않고, 환경 변수나 구성 파일에 저장하고, 이를 암호화된 형태로 관리합니다.
- 키 저장소: 키 관리 시스템(KMS), 하드웨어 보안 모듈(HSM) 또는 클라우드 기반의 키 저장소 서비스를 사용하여 키를 보호합니다.
키 관리:
- 정기적인 키 교체: 정기적으로 키를 변경(키 롤오버)하여 보안을 강화합니다.
- 액세스 제어: 키에 대한 액세스를 엄격하게 제어하며, 키 사용 권한이 있는 개인이나 서비스를 제한합니다.
- 감사 및 로깅: 키 사용에 대한 로깅을 활성화하여 비정상적인 활동을 모니터링합니다.
Java 예제 코드 (키 생성):
import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import java.security.SecureRandom; import java.util.Base64; public class KeyGen { public static void main(String[] args) throws Exception { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); SecureRandom random = new SecureRandom(); // CSPRNG keyGen.init(256, random); // AES 키를 256비트로 설정 SecretKey secretKey = keyGen.generateKey(); // 키를 Base64 인코딩하여 출력(저장 또는 전달에 용이) String encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded()); System.out.println("Generated Key (Base64): " + encodedKey); } }
이 코드는 KeyGenerator를 사용하여 안전하게 AES 키를 생성하고, 생성된 키를 Base64 문자열로 인코딩하여 출력합니다. 이 문자열은 환경 변수에 저장하거나, 키 관리 시스템에 안전하게 보관할 수 있습니다.
실제 환경에서는 키를 코드에 직접 포함시키지 않으며, 애플리케이션이 실행될 환경에 키를 안전하게 배치해야 합니다. 또한, 키의 라이프사이클을 관리하고, 적절한 권한을 가진 사용자만이 키에 접근할 수 있도록 해야 합니다.
애플리케이션이 실행될 환경에 키를 안전하게 배치, 어떻게?
아래 내용을 어떻게 해야 하는 지 궁금했어요.
실제 환경에서는 키를 코드에 직접 포함시키지 않으며, 애플리케이션이 실행될 환경에 키를 안전하게 배치해야 합니다
[질문] 안녕 ChatGPT, 다음의 사항을 구현하는 방법을 알려줄래?
실제 환경에서는 키를 코드에 직접 포함시키지 않으며, 애플리케이션이 실행될 환경에 키를 안전하게 배치해야 합니다
[답변]
실제 환경에서 암호화 키를 코드에 직접 포함시키지 않는 이유는 보안상의 위험 때문입니다. 코드가 노출되었을 때 키도 같이 노출되어 보안이 취약해지기 때문입니다. 이를 방지하기 위해 여러 방법으로 키를 안전하게 배치하고 관리할 수 있습니다:
1. 환경 변수:
애플리케이션이 실행되는 환경(예: 서버)의 환경 변수에 암호화 키를 저장합니다. 이 방법은 키를 코드와 분리하여 관리할 수 있게 하며, 코드 노출 시 키가 함께 노출되는 것을 방지합니다.
예시:
export APP_ENCRYPTION_KEY="your-encryption-key-here"
Java 애플리케이션에서는 System.getenv("APP_ENCRYPTION_KEY")를 통해 이 값을 읽어올 수 있습니다.
2. 구성 파일:
애플리케이션 구성 파일(예: .properties, .yml 파일)에 키를 저장할 수도 있습니다. 이 파일들은 애플리케이션과 함께 배포되지만, 소스 코드 관리 시스템에는 포함되지 않아야 합니다.
예시: application.properties
app.encryption-key=your-encryption-key-here
Spring Framework에서는 @Value("${app.encryption-key}") 어노테이션을 사용하여 이 값을 주입받을 수 있습니다.
3. 외부 키 관리 시스템:
AWS Key Management Service(KMS), HashiCorp Vault, Azure Key Vault와 같은 키 관리 시스템을 사용하여 암호화 키를 안전하게 저장하고 관리할 수 있습니다. 이러한 시스템들은 키의 생성, 저장, 로테이션, 사용에 대한 감사 로깅 등을 제공합니다.
4. 하드웨어 보안 모듈(HSM):
보안이 매우 중요한 환경에서는 HSM을 사용할 수 있습니다. HSM은 물리적인 보안 장치에서 암호화 키를 생성하고 저장하며, 키가 절대로 장치를 떠나지 않게 합니다.
5. 암호화된 저장소:
키를 파일에 저장할 경우, 디스크 암호화 기술을 사용하여 저장소 자체를 암호화할 수 있습니다. 예를 들어, Linux의 LUKS나 Windows의 BitLocker가 있습니다.
6. 액세스 제어:
키에 대한 액세스는 필요한 최소한의 인원에게만 허용되어야 하며, 이를 위해 사용자 권한 및 역할 기반 액세스 제어(RBAC)를 설정합니다.
7. 로테이션과 백업:
암호화 키는 정기적으로 교체되어야 하며(키 로테이션), 안전한 위치에 백업되어야 합니다. 키 교체는 가능한 자동화되어야 하며, 백업은 재난 복구 계획의 일부로 관리되어야 합니다.
암호화 키를 관리할 때는 항상 보안 최고 사례를 따르고, 적절한 보안 감사 및 컴플라이언스 검증을 거치는 것이 중요합니다.
이후 ChatGPT에 물어보고 그 내용을 검토하고
검토할 때 궁금한 사항은 또 물어봤어요.
하루만에 기능 개발을 완료했어요.
늦은 시간까지 ChatGPT와 많은 대화를 하며 새로운 내용도 많이 배우면서
차근차근 하나씩 코드들을 시험하고 수정했어요.
물론 수정은 ChatGPT가 했어요.
아래는 ChatGPT를 활용해서 만든 코드이고 클라이언트 요구사항에 맞게 개발이 되었어요.
너무도 대단하게 클라이언트와 서버 사이드 코드를 잘 만들어줬어요.
저는 코드 한 줄 추가하지 않았어요.
[서버 사이드 코드]
package com.example.securedatatransfer.controller;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import jakarta.servlet.http.HttpSession;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Optional;
import jakarta.servlet.http.HttpServletRequest;
@RestController
public class FileUploadController {
private static final Logger logger = LoggerFactory.getLogger(FileUploadController.class);
private static final String UPLOAD_DIR = "uploads/";
@PostMapping("/upload-encrypted-file")
public String uploadEncryptedFile(
@RequestParam("file") MultipartFile encryptedFile,
@RequestParam("iv") MultipartFile ivFile,
@RequestParam("filename") String filename, // 파일 이름을 받는 파라미터
HttpSession session) {
logger.info("Received an upload request with an encrypted file.");
try {
// 세션에서 암호화 키 가져오기
String sessionKey = (String) session.getAttribute("SESSION_KEY");
if (sessionKey == null) {
logger.error("Session key is missing for file upload.");
throw new IllegalArgumentException("Session key is missing.");
} else {
logger.info("Session key is present: {}", sessionKey);
}
// 받은 IV와 암호화된 파일 데이터를 바이트 배열로 변환
byte[] iv = ivFile.getBytes();
byte[] encryptedBytes = encryptedFile.getBytes();
logger.info("IV (Base64 encoded) and encrypted file bytes retrieved: {}", iv);
// 복호화를 위한 IV와 키 준비
SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(sessionKey), "AES");
IvParameterSpec ivParams = new IvParameterSpec(iv);
logger.info("Key specification and IV parameter spec prepared.");
// 복호화
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParams);
logger.info("Cipher initialized for decryption.");
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
logger.info("Decryption process completed.");
// 복호화된 데이터로부터 엑셀 워크북 객체 생성
try (XSSFWorkbook workbook = new XSSFWorkbook(new ByteArrayInputStream(decryptedBytes))) {
logger.info("Excel workbook created from decrypted data.");
// 파일 경로 생성
Path path = Paths.get(UPLOAD_DIR, filename);
Files.createDirectories(path.getParent());
logger.info("File path created: {}", path);
// 엑셀 파일 저장
try (FileOutputStream outputStream = new FileOutputStream(path.toFile())) {
workbook.write(outputStream);
logger.info("Excel file written and saved successfully.");
}
}
return "File decrypted and saved successfully as Excel.";
} catch (Exception e) {
logger.error("Error during decryption or Excel file saving: {}", e.getMessage(), e);
return "Error during decryption or Excel file saving";
}
}
// AES 복호화 메소드
private byte[] decrypt(byte[] cipherData, String key) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(key);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[cipher.getBlockSize()]; // 예시로 블록 크기와 동일한 0으로 초기화된 IV를 사용합니다.
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParams);
return cipher.doFinal(cipherData);
}
}
[클라이언트 사이드 코드]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Encrypt and Upload File</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
</head>
<body>
<input type="file" id="fileInput" />
<button id="uploadButton">Upload</button>
<script>
// 파일을 암호화하고 서버로 업로드하는 함수
async function encryptAndUploadFile(file, publicKey, fileName) {
const reader = new FileReader();
reader.onload = async function(event) {
try {
console.log('Data loaded:', event.target.result); // 데이터 로딩 확인
const data = event.target.result;
// 세션키 생성 확인
const sessionKey = CryptoJS.lib.WordArray.random(128/8);
// IV 생성 확인
const iv = CryptoJS.lib.WordArray.random(128/8);
// 암호화된 데이터 확인
const encrypted = CryptoJS.AES.encrypt(CryptoJS.lib.WordArray.create(data), sessionKey, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
console.log('Encrypted data created');
// 서버의 공개키를 이용하여 세션키를 암호화
const encryptedSessionKey = await encryptWithPublicKey(publicKey, sessionKey);
console.log('Encrypted session key created');
// 암호화된 파일 전송
console.log('File encrypted. Preparing to send...');
await sendEncryptedFileToServer(encrypted.ciphertext, encryptedSessionKey, iv, fileName);
} catch (error) {
console.error('Error encrypting file:', error);
}
};
reader.onerror = function(error) {
console.error('Error reading file:', error);
};
console.log('Starting file encryption...');
reader.readAsArrayBuffer(file);
}
// 서버의 공개키를 사용하여 세션키를 암호화합니다.
async function encryptWithPublicKey(publicKey, sessionKey) {
try {
// Base64 인코딩된 공개키를 ArrayBuffer로 디코딩합니다.
const publicKeyBuffer = Uint8Array.from(atob(publicKey), c => c.charCodeAt(0));
// 웹 암호화 API를 사용하여 공개키를 가져옵니다.
const cryptoKey = await window.crypto.subtle.importKey(
'spki',
publicKeyBuffer,
{
name: 'RSA-OAEP',
hash: {name: 'SHA-256'},
},
false,
['encrypt']
);
// CryptoJS의 WordArray를 ArrayBuffer로 변환합니다.
const sessionKeyBuffer = wordArrayToArrayBuffer(sessionKey);
// 세션키를 공개키로 암호화합니다.
const encryptedSessionKeyBuffer = await window.crypto.subtle.encrypt(
{ name: 'RSA-OAEP' },
cryptoKey,
sessionKeyBuffer
);
return new Uint8Array(encryptedSessionKeyBuffer);
} catch (error) {
console.error('Error encrypting session key:', error);
throw error;
}
}
function wordArrayToArrayBuffer(wordArray) {
const { words, sigBytes } = wordArray;
const bytes = [];
for (let i = 0; i < sigBytes; i++) {
const byte = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
bytes.push(byte);
}
return new Uint8Array(bytes).buffer;
}
// 암호화된 파일과 IV를 서버로 전송하는 함수
async function sendEncryptedFileToServer(encryptedData, encryptedSessionKey, iv, fileName) {
function wordArrayToUint8Array(wordArray) {
const len = wordArray.sigBytes;
const words = wordArray.words;
const u8_array = new Uint8Array(len);
let offset = 0, word, i;
for (i=0; i<len; i++) {
word = words[i];
u8_array[offset++] = word >> 24;
u8_array[offset++] = (word >> 16) & 0xff;
u8_array[offset++] = (word >> 8) & 0xff;
u8_array[offset++] = word & 0xff;
}
return u8_array;
}
const formData = new FormData();
const ivBlob = new Blob([wordArrayToUint8Array(iv)], { type: 'application/octet-stream' });
const encryptedDataBlob = new Blob([wordArrayToUint8Array(encryptedData)], { type: 'application/octet-stream' });
formData.append('file', encryptedDataBlob);
formData.append('sessionKey', new Blob([encryptedSessionKey], { type: 'application/octet-stream' }));
formData.append('iv', ivBlob);
formData.append('filename', fileName);
try {
const response = await fetch('https://localhost:8443/asymmetric-encrypted-file', {
method: 'POST',
body: formData,
credentials: 'include'
});
if (!response.ok) {
throw new Error(`File upload failed with status: ${response.status}`);
}
const data = await response.text();
console.log('Server response:', data);
} catch (error) {
console.error('Error:', error);
}
}
// 업로드 버튼 클릭 시 파일 암호화 및 업로드 처리
document.getElementById('uploadButton').addEventListener('click', async () => {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (!file) {
alert('Please select a file to upload.');
return;
}
const publicKey = await getPublicKey();
const fileName = file.name; // 파일 이름을 그대로 사용하거나 수정하여 사용할 수 있습니다.
console.log('Uploading file...');
await encryptAndUploadFile(file, publicKey, fileName);
});
async function getPublicKey() {
// 서버로부터 공개키를 가져오는 코드
const response = await fetch('https://localhost:8443/public-key');
if (!response.ok) {
throw new Error(`Could not fetch the public key: ${response.statusText}`);
}
const publicKeyBase64 = await response.text();
return publicKeyBase64;
}
</script>
</body>
</html>
ChatGPT가 모든 걸 다 해주지는 않아요.
챗GPT라고 해서 아무 말을 해도 다해주는 그런 마법사는 아니예요
챗GPT가 답변하는 내용을 시작으로 해서
우리가 원하는 목적지에 닿을 때까지 ChatGPT가 쉽게 이해할 수 있도록 도와줘야 해요.
그래서 이제는 코딩하는 능력보다 요구 사항을 잘 설계하고 창의적으로 문제를 바라보는 능력이 더 중요할 것 같아요.
지금까지,
언제나 성장하는 인텔리원스튜디오(IntelliOneStudio)입니다.
감사합니다.
'디지털노마드' 카테고리의 다른 글
[챗GPT게임개발2] ChatGPT를 활용하여 나만의 캐릭터로 게임 만들기 (99) | 2024.04.28 |
---|---|
[챗GPT게임개발1] ChatGPT를 활용하여 Unity 플랫포머 게임 알아보기 (115) | 2024.04.27 |
[API개발] GPTs를 활용하여 나만의 API 전문가 만들기 (94) | 2024.04.26 |
[챗GPT활용개발] ChatGPT를 활용하여 AdMob 리워드 광고 추가 하기 (88) | 2024.04.25 |
[5분개발] ChatGPT를 활용하여 소스 코드 수정하기 (83) | 2024.04.25 |