1. Prerequisite – 준비
앞선 2개의 글을 통해 Firebase에 처음 가입하고, 초기설정하는 방법과, Visual Studio에 SDK를 연결하는 방법을 소개한 바 있었다. 이 과정에서 Firebase가 제공하는 index.html 템플레이트 파일도 준비가 되었는데, 앞선 글에서는 이 index.html 파일에 Bootstrap 사용을 위한 CSS와 JS, jQuery CDN Link, 그리고 Firebase configuration 정보를 추가하여 본격적인 Firebase 사용을 위한 준비를 마쳤다.
이러한 과정이 생소한 경우에는 앞선 2개의 글을 먼저 참조하는 것이 바람직하다.
우선, 처음 Firebase에 처음 가입하고, 필요한 빌드 기능들을 추가하여 초기화하는 것과, Visual studio에 SDK를 연결하는 과정은 다음 글을 참조하고,
https://blog.naver.com/mythee1/223491033437
본격적인 사용을 위해 index.html 파일에 Bootstrap CSS와 JS Link, 그리고 jQuery CDN Link와 Firebase 접속에 필요한 Firebase configuration 정보 추가 부분은 다음의 글을 참고하기 바란다.
https://blog.naver.com/mythee1/223492208854
2. Firestore Database 사용 – 기본 이론
우선, 앞선 초기 설정 단계에서 다음과 같이 Firestore 내에 “products”라는 이름의 컬렉션(=DB)를 만들어두었다. (참고: Firebase에서는 DB를 컬렉션이라고 부른다.)
이렇게 만든 컬렉션(=DB)에 자료를 추가하는 방법은 Firebase의 공식 문서(https://firebase.google.com/docs/firestore?authuser=0&hl=ko)에 다음과 같이 자세히 설명되어 있다.
이 문서에는 사용하는 플랫폼에 따라
초기 선언하는 방법과,
레코드의 이름을 특정해서 지정할 때 사용하는
db.collection(컬렉션이름).doc(문서이름=레코드구분이름).set().then().catch() 명령어 사용예와,
그리고 레코드의 이름을 특정해서 지정할 필요가 없는 경우에 편하게 사용하는
db.collection(컬렉션이름).add().then().catch() 명령어 사용 예를 소개한다.
또한, 여러 필드의 데이터를 하나의 개체로 구조화하는 방법에 대한 사용 예도 소개한다.
3. Copilot / GPT로 자료 추가 개발하기
위에서 컬렉션에 데이터 문서를 추가하는 기본 방법을 확인하기는 했으나, 본격적인 개발자도 아닌 내 입장에서는 이러한 방식대로 일일이 코드를 만들기보다는, 편법을 이용해서 코드를 만들기로 했다. 바로, ChatGPT의 기술을 활용했다는 마이크로소프트의 Copilot을 이용하기로 했다. (참고: MS365 사용자는 Edge 브라우저에서 사용이 가능하다.) 본 글에서는 이처럼 Coilot을 활용해서 Firestore에 자료(문자, 사진)를 올리는 코드를 만드는 과정을 소개한다.
참고로 Copilot은 개발자들이 코드 개발에 가장 많이 사용하는 AI로 알려져 있는데, ChatGPT-4를 만든 개발사인 OpenAI사에 마이크로소프트사가 막대한 (100억불 이상이라고 하더라..) 투자를 하여 지분을 확보한 기술에, 마이크로소프트사가 인수한 또다른 업체인 Github의 수많은 오픈소스 코드로 학습한 덕택에 프로그램 개발에 차별적인 강점이 있다고 하는것 같다.
먼저 MS의 Edge를 통해 Copilot에 접속한다. 화면 아래에 Copilot 질문 창의 나타나는데, 한 가지 주의할 것은 답변 모드를 “보다 정밀한“으로 선택해야 한다.
다음과 같이 질문해 본다.
다음과 같이 코드를 보여준다.
위의 내용을 내 Visual studio Code의 Index.html의 <script>..</script> 부분에 붙여 넣고, 이것을 다시 Copilot에 입력하면서 1차 검토를 받아본다.
그 결과 다음과 같은 Feedback을 받았다.
위의 코드에는 실제 자료를 업로드하는 부분은 없었기에, 다음과 같이 추가 요청하고
다음과 같은 코드를 받게 된다.
이제 이를 붙여 넣고, 브라우저를 살펴보면 다음과 같이 화면 표시가 이루어진다.
이 폼을 개선하기 위해 다음과 같은 요청을 몇 번 반복했다.
그 결과, 폼 부분에 대해 다음과 같이 간결하게 만들어진 코드를 돌려주었고,
화면은 다음과 같이 바뀌었다.
여기서 코드에 사용 중인 id 들과, 버튼 색상을 변경해 달라고 했고, 다음과 같이 수정된 코드를 받았으며,
화면은 이렇게 변경되었다. 이로써 자료 입력 form은 어느 정도 원하는 대로 된 것 같았다.
이제 업로드 부분의 코드를 변경하기 위해 질문했다.
그런데 돌려준 코드가 잠시 쉬었다 질문해서였는지 이상했다.
이 부분에 대해 지적했더니,
다음과 같이 feedback을 하면서 수정된 코드를 제공해 주었다.
이를 가지고 화면에서 다음과 같이 입력을 시도했으나, 개발자 화면에 “권한 오류”가 보였다.
이 부분은 파일을 Firebase의 스토리지에 저장할 때 권한에 대한 부분인 것을 알고 있었기에, 다음과 같이 콘솔에 들어가 다음 부분을
아래와 같이 변경하고
다시 브라우저로 돌아와 업로드 버튼을 누르니, 잘 저장되었다는 메시지가 나온다.
이 메시지 부분에는 업로드한 사진 파일이 저장된 URL 주소와, 입력한 자료들이 내가 만들어둔 products라는 이름의 컬렉션(=DB)에 어떤 레코드 이름으로 저장되었는지가 ID라는 이름으로 표시된다.
이제 Firestore 콘솔로 들어가 확인해 보니, 해당 ID로 문서(=레코드)가 추가된 것과, 그 데이터들이 입력한 대로 기록된 것을 볼 수 있다. (참고: Firestore에는 각 레코드를 “문서” 또는 Document라고 부른다.)
기록된 URL을 복사하여,
브라우저에서 열어보니, 업로드한 가지 사진이 정상적으로 나타난다.
이 상태로 원하는 기능들이 모두 충족되기는 했지만…
다소 보완할 부분이 있는듯해서, 다음과 같이 코드를 변경해달라고 요청했다. 입력된 키워드들이 업로드되고 나면, 지워지는 게 정상적인듯했고, Code도 가급적 공식 문서에서 소개한 방법을 따르고 싶었기 때문이다.
그 결과 Copilot은 수정된 코드를 돌려주었고,
이를 복사해서 붙여 넣은 후, 전체 코드를 다시 입력해 넣고, 검토를 요청했다.
그랬더니, 다음과 같이 아무 문제가 없다고 한다.
그런데, 이 상태에서 다시 한번 레코드 입력을 시도했더니 다음과 같은 에러 메시지가 발생했다.
이를 Copilot에게 알렸더니, 다음과 같이 보완할 부분이 있다고 하면서 수정된 코드를 제공해 준다.
다시 수정 코드를 반영하고, 실행해 보니, 이제 오류메시지는 사라지고 말끔하게 동작한다.
또한 정상적으로 업로드가 이루어진 후, 입력한 사항들도 모두 리셋되는 것도 확인된다.
Firestore 콘솔에서 확인해 보니, 입력한 사항들도 모두 정상적으로 등록되어 있고,
사진 파일도 URL을 통해 정상적으로 열리는 것을 확인할 수 있었다.
4. 저장되는 파일 이름의 유일성 확보
위와 같은 코드를 사용하면 자료와 사진을 Firestore DB에 문제없이 등록할 수 있다.
다만, Storage에 저장되는 그림파일의 이름에 대해 한가지 고려해야 할 사항이 있었다.
만일 동일한 이름을 가진 파일이 등록되면, 기존에 storage에 등록되었던 파일을 overwrite하는 문제가 있다. 원래 의도한 것과 다르게, 단순히 파일 이름이 같다는 이유만으로 전혀 다른 파일로 대체될 문제가 있는 것이다.
또한 다음과 같이 PC상의 동일한 사진을 여러 레코드에 파일이름을 바꾸지 않고 반복해서 등록한경우, 자료를 읽어올때는 문제가 없지만
만일 이들중 하나의 레코드를 삭제하면, 해당 사진도 삭제되면서 관련된 다른 레코드에서도 사진이 사라지는 문제가 발생한다.
이러한 이유로, Storage에 저장되는 파일의 이름은 유일하게 지정되어야만 한다.
본 글에서는 timestamp를 이용해서, 파일이름을 새로 생성해 원래의 파일 이름 대신 사용하기로 했고, 이미지 파일을 읽어 업로드 하는 부분을 다음과같이 수정했다.
최종 코드는 다음과 같다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Welcome to Kongyi2 Project</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-firestore.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-storage.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<div class="container">
<form id="productForm" class="m-3">
<input type="text" id="title" name="title" class="form-control mb-3" placeholder="상품명">
<input type="text" id="price" name="price" class="form-control mb-3" placeholder="가격">
<textarea id="content" name="content" rows="4" class="form-control mb-3" placeholder="상품설명"></textarea>
<input type="file" id="pimage" name="pimage" class="form-control mb-3">
<input type="submit" value="업로드" class="btn btn-primary" style="background-color: orange;">
</form>
</div>
<script>
const firebaseConfig = { 접속정보
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
var db = firebase.firestore();
var storage = firebase.storage();
$(document).ready(function() {
$('#productForm').on('submit', function(e) {
e.preventDefault();
var pname = $('#title').val();
var price = $('#price').val();
var description = $('#content').val();
var pimage = $('#pimage').get(0).files[0];
var timestamp = new Date().getTime();
var fileName = 'image_' + timestamp + '.jpg';
var storageRef = firebase.storage().ref('products/' + fileName);
var uploadTask = storageRef.put(pimage);
uploadTask.on('state_changed', function(snapshot){
// 업로드 상태를 모니터링할 수 있습니다.
}, function(error) {
// 업로드 중 오류가 발생했을 때 처리합니다.
console.error("Error uploading image: ", error);
}, function() {
// 이미지 업로드가 성공적으로 완료되면, 이미지 URL을 가져옵니다.
uploadTask.snapshot.ref.getDownloadURL().then(function(downloadURL) {
console.log('File available at', downloadURL);
// 파이어베이스 파이어스토어에 데이터 업로드
db.collection("products").add({
상품명: pname,
가격: price,
상품설명: description,
사진: downloadURL,
날짜: new Date() // 현재 시간을 기록합니다.
})
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
// 데이터가 업로드된 후에는 폼의 내용을 리셋합니다.
$('#productForm')[0].reset();
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
});
});
});
});
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
5. 후기
이렇게 Copilot을 통해 자료등록 코드를 만들어보니, 웹 프로그래밍을 거의 모르는 입장에서 1시간이 안되는 시간 내에 코드를 완성할 수 있었다.
그렇다고 Copilot이 완벽하다고 느껴진 것은 아니었다. 마치 잘 아는 전문가 과이 선생님 같은 느낌이었는데, 얼마나 정확하고 세세하게 질문하고 요청하는가에 따라 반환되는 결과도 좀 달라지는 듯했다. 그리고 처음에는 좀 제한적으로만 답변하는 느낌을 받았다.
원하는 것을 상세하게, 그리고 반복해서 요청할수록, 좀 더 원하는 바에 가까운 결과가 가능하다고 느껴졌다.

