오늘 게임 서버 개발자 1주차 과제를 끝냈다.
내가 맡은 역할은 팀 소개 HTML의 입력 값을
FireBase DataBase에 저장하고, 조회할 수 있게
만드는 것이었다...
하지만, HTML을 담당한 팀원의 능력이 뛰어나서
기존 강의 내용을 바탕으로 FireBase DataBase에
저장이 불가능했다.
(html 파일과 javascript 파일을 따로 분리시켜 만드는 방식을 사용하셨다)
그래서 결국 HTML을 담당한 팀원이 DataBase 까지 구축하고
값 저장 및 조회를 완성하셨다.
그래서, 그 분이 만든 HTML, JavaScript, DataBase 코드를 뜯어보고
따라쳐보면서 내 것으로 만들어볼려고 한다.
들어가기 앞 서
디렉토리 내부 구성을 먼저 살펴보면,
directory 내부느
는 다음과 같이 구성 돼 있다.
HTML HEAD
(팀원님의 코드)
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>팀 페이지</title>
<link rel="stylesheet" href="styles.css">
<script type="module">
import { openAddModal, addMember, openModal, closeModal, saveMemberData, deleteMember, closeAddModal } from './script.js';
window.openAddModal = openAddModal;
window.addMember = addMember;
window.openModal = openModal;
window.closeModal = closeModal;
window.saveMemberData = saveMemberData;
window.deleteMember = deleteMember;
window.closeAddModal = closeAddModal;
</script>
</head>
(/팀원님의 코드)
기존 수업 내용과 import 방식이 다른 것을 볼 수 있다.
(기존 코드)
<head>
<script type="module">
// Firebase SDK 라이브러리 가져오기
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "AIzaSyCz-aTF0iQvQilKJyrTHQxg1T2X_13trtw",
authDomain: "sparta-49ce5.firebaseapp.com",
projectId: "sparta-49ce5",
storageBucket: "sparta-49ce5.appspot.com",
messagingSenderId: "819019173056",
appId: "1:819019173056:web:e47a0781c0b38268923125",
measurementId: "G-0NFWX91DD1"
};
// Firebase 인스턴스 초기화
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
</script>
</head>
(/기존 코드)
import { openAddModal, addMember, openModal, closeModal, saveMemberData, deleteMember, closeAddModal } from './script.js';
팀원님의 코드는 import를 './script.js' 로 우리가 작업하는 디렉토리 내부에서 가져오는 것을 볼 수 있다.
JavaScript DB Import 방식
간략하게 JavaScript Import 방식을 보면
const firebaseConfig = {
apiKey: "AIzaSyDFHJXiFM2YtnoDYIYZL9tFH7fi2gSRuXA",
authDomain: "sparta-d46a3.firebaseapp.com",
projectId: "sparta-d46a3",
storageBucket: "sparta-d46a3.appspot.com",
messagingSenderId: "736869860855",
appId: "1:736869860855:web:f5e7e878753119d06f5af2",
measurementId: "G-4LND2EJ73P"
};
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
HTML에서 <script type='module'></script> 내부에 적은 내용이 모두
JavaScript로 옮겨진 것을 볼 수 있다.
(중요)
JavaScript에서 import, FireBase 정보,
initializeApp() 함수
(
Firebase에서 제공하는 다양한 서비스를
'firebaseConfig' 객체를 입력 받아 초기화 한다.
)
, getFirestore() 함수
('app'객체를 입력 받아 데이터 베이스를 초기화 해줍니다.)
로 작성 돼 있는 것을 볼 수 있다.
(/중요)
JavaScript에서 HTML로 Import 받은 내용은
window.openAddModal = openAddModal;
window.addMember = addMember;
window.openModal = openModal;
window.closeModal = closeModal;
window.saveMemberData = saveMemberData;
window.deleteMember = deleteMember;
window.closeAddModal = closeAddModal;
"window.내용"은 window 객체의 함수로 import 받은 내용을 속성으로 저장 합니다.
(
window 객체 : 웹 브라우저 환경에서의
모든 전역 객체와 함수,
브라우저 창과 관련된 기능,
사용자와 상호작용하는
다양한 방법을 포함하는 핵심 객체 입니다.
)
window.openAddModal
은 모달(대화 상자, 작은 창) 창을 여는 기능을
수행할 수 있도록 기능을 추가해줍니다.
window.addMember
은 데이터베이스에 새로운 값을 저장하거나,
목록을 갱신하는 작업이 가능하게 기능을 추가해줍니다.
window.openModal
은 모달 창을 여는 기능을 수행하는 함수 입니다.
window.closeModal
은 모달 창을 닫는 기능을 수행하는 함수 입니다.
window.saveMemberData
은 데이터베이스에 새로운 데이터을 저장하는 함수 입니다.
window.deleteMember
은 데이터베이스에 데이터를 삭제하는 함수 입니다.
window.closeAddModal
은 모달 창을 닫는 기능을 수행하는 함수 입니다.
위의 window 기능을 바탕으로
HTML <header></header>와 <body></body>에
없는 모달(대화 상자, 작은 창) 창이 열리고 입력된 데이터가
데이터베이스에 저장될 수 있게 해줍니다.
예를 들어,
<div id="modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeModal()">×</span>
<div class="modal-inner-content">
<div class="modal-photo" id="modal-photo">사진</div>
<div class="modal-info">
<h3 id="modal-name"></h3>
<div class="modal-description">
<label>설명</label>
<textarea id="modal-description"></textarea>
</div>
<div class="modal-strength">
<label>나의 장점</label>
<textarea id="modal-strength"></textarea>
</div>
<div class="modal-style">
<label>협업 스타일</label>
<textarea id="modal-style"></textarea>
</div>
<div class="modal-buttons">
<button id="postingBtn" class="save-button" onclick="saveMemberData()">등록</button>
<button class="cancel-button" onclick="closeModal()">취소</button>
</div>
</div>
</div>
</div>
</div>
위와 같은 코드를 살펴 보면,
<div id="modal" class="modal">
</div>
<header></header>와 <body></body> 사이에 없어도
모달창이 뜨는 것을 볼 수 있다.
모달창을 띄워주는 버튼이 눌릴 경우
<div class="buttons">
<button class="add-button" onclick="openAddModal()">추가</button>
</div>
앞에서 import 및
window.openAddModal
를 추가시켜줬기 때문에
onclick으로 "openAddModal()"함수가 호출됐기 때문이다.
이제 모달창 내부를 좀 더 살펴보면,
<div id="add-modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeAddModal()">×</span>
<div class="modal-inner-content">
<div class="modal-photo">
<label for="photo-input">프로필 사진</label>
<input type="file" id="photo-input" accept="image/*">
</div>
<div class="modal-info">
<div class="modal-description">
<label for="name-input">이름</label>
<input type="text" id="name-input">
</div>
<div class="modal-description">
<label for="description-input">자기소개</label>
<textarea id="description-input"></textarea>
</div>
<div class="modal-description">
<label for="mbti-input">MBTI</label>
<input type="text" id="mbti-input">
</div>
<div class="modal-buttons">
<button class="save-button" onclick="addMember()">등록</button>
<button class="cancel-button" onclick="closeAddModal()">취소</button>
</div>
</div>
</div>
</div>
</div>
<div 옆에 id와 class가 있는데
class="modal"은
우리가 만들어준 styles.css 내부의
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
animation: fadeIn 0.5s;
}
를 가져와 모달의 사이즈를 지정 해주고,
모달의 배경 색상 등을 변경하면서
꾸밀 수 있게 해주는 속성이다.
(
다른 테그들 또한 위와 같은 방식으로
꾸미기와 사이즈 조절이 가능하다.
)
id는
으로 프로필 사진과 텍스트들이 입력된 상태에서
"등록" 버튼이 클릭 될 경우 JavaScript로
HTML 테그의 입력 값들이 넘어가게 해주는데
<div class="modal-buttons">
<button class="save-button" onclick="addMember()">등록</button>
<button class="cancel-button" onclick="closeAddModal()">취소</button>
</div>
버튼 테그의 onclick="addMember()"함수가 동작하기 때문이다.
saveMemberData()함수는
script.js에서 내부에 구현돼 있다.
addMember()함수를 살펴보면,
export async function addMember() {
var name = document.getElementById("name-input").value;
var description = document.getElementById("description-input").value;
var mbti = document.getElementById("mbti-input").value;
var photoInput = document.getElementById("photo-input");
var githubUrl = prompt("Github URL을 입력하세요:");
var reader = new FileReader();
reader.onload = async function(e) {
var photoUrl = e.target.result;
await addDoc(collection(db, '컬렉션 이름'), {
name: name,
description: description,
mbti: mbti,
photoUrl: photoUrl,
githubUrl: githubUrl
});
loadMembers();
closeAddModal();
}
if (photoInput.files[0]) {
reader.readAsDataURL(photoInput.files[0]);
} else {
alert("프로필 사진을 선택해주세요.");
}
}
export async function addMember() {
export async function addMember()라고
다른 함수에서는 못 보던 export가 있는걸 볼 수 있는데
export 는 JavaScript에서 특정 모듈, 내부 변수,
내부 함수, 내부 클래스, 내부 객체를
다른 모듈이나 파일에서
사용할 수 있게 내보내는 역할을 합니다.
이제 addMember()함수 내부를 살펴보면
const 변수명 = document.getElementById('~~~).value
로 돼 있는데
이는
<div id="add-modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeAddModal()">×</span>
<div class="modal-inner-content">
<div class="modal-photo">
<label for="photo-input">프로필 사진</label>
<input type="file" id="photo-input" accept="image/*">
</div>
<div class="modal-info">
<div class="modal-description">
<label for="name-input">이름</label>
<input type="text" id="name-input">
</div>
<div class="modal-description">
<label for="description-input">자기소개</label>
<textarea id="description-input"></textarea>
</div>
<div class="modal-description">
<label for="mbti-input">MBTI</label>
<input type="text" id="mbti-input">
</div>
<div class="modal-buttons">
<button class="save-button" onclick="addMember()">등록</button>
<button class="cancel-button" onclick="closeAddModal()">취소</button>
</div>
</div>
</div>
</div>
</div>
모달창 HTML에 id="~~~~"들을
JavaScript로 가져온 것이다.
이름 부분 HTML
<input type="text" id="name-input">
이름 부분 JavaScript
const name = document.getElementById('modal-name').value;
자기소개 부분 HTML
<textarea id="description-input"></textarea>
자기소개 부분 JavaScript
const description = document.getElementById('modal-description').value;
mbti 부분 HTML
<input type="text" id="mbti-input">
mbti 부분 JavaScript
const strength = document.getElementById('modal-strength').value;
프로필 부분 HTML
<input type="file" id="photo-input" accept="image/*">
프로필 부분 JavaScript
var photoInput = document.getElementById("photo-input");
위와 같이 HTML 테그 id="~~~" 부분과
document.getElementById("~~~") 부분의
"~~~"이 같아야 한다.
이미지 같은 경우 입력 유무를 판단하기 위해
var reader = new FileReader();
reader.onload = async function(e) {
var photoUrl = e.target.result;
await addDoc(collection(db, '컬렉션 이름'), {
name: name,
description: description,
mbti: mbti,
photoUrl: photoUrl,
githubUrl: githubUrl
});
loadMembers();
closeAddModal();
}
if (photoInput.files[0]) {
reader.readAsDataURL(photoInput.files[0]);
}
else {
alert("프로필 사진을 선택해주세요.");
}
var reader = new FileReader() 클래스로
파일 Reader 클래스를 생성해
reader.onload 함수를 async(비동기) function(e)
함수로 파일을 읽어 와서
var photoUrl = e.target.result
로 이미지의 URL을 가져올 수 있었다.
이후 import 받은 addDoc함수를 사용해
addDoc(collection("사용할 DB", "Collection Name"(관계형 DB에서는 Table에 해당함))
{
저장할 데이터들
}
위와 같은 방법으로 데이터들을 저장할 수 있었다.
하지만,
if(photoInput.fiiles[0]) reader.readAsDataURL(photoInput.files[0]);
(사용자가 파일을 선택했을 경우,
FileReader 객체를 사용해 파일을 읽어옵니다.
else alert("프로필 사진을 선택해주세요")
이미지를 선택하지 않은 경우
알림으로 "프로필 사진을 선택해주세요"라는
문구를 띄워줍니다.
이후, loadMembers() 비동기 함수를 호출해줍니다.
async function loadMembers() {
const memberSection = document.getElementById('team-members');
memberSection.innerHTML = '';
const membersCol = collection(db, '컬렉션 이름');
const membersSnapshot = await getDocs(membersCol);
const memberList = membersSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
memberList.forEach(member => {
const memberDiv = createMemberElement(member);
memberSection.appendChild(memberDiv);
});
}
loadMembers() 비동기 함수에서는
const memberSection = document.getElementById('team-members');
으로 HTML 문서에서 id가 "team-members"인 요소를 찾아와
(
HTML에서 id가 "team-members"인 부분
<section class="team-members" id="team-members">
</section>
(특정한 주제나 기능을 가진 콘텐츠 블록을 그룹화하는 데 사용)
(해당 부분 섹션 입니다.)
)
memberSection.innerHTML = '';
섹션 내에 있는 모든 것들을 지워주고
(향후 DB의 컬렉션 안에 있는 요소를 출력해 줄 계획인데 중복 방지를 위해 모두 삭제)
이후,
const membersCol = collection(db, '컬렉션 이름');
FireBase의 db에서 "컬렉션"을 가져오고
const membersSnapshot = await getDocs(membersCol);
await(비동기) getDocs(import 받은 내용) DB 기능으로
컬랙션의 모든 문서를 개체로 가져옵니다.
다음으로
const memberList = membersSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
membersSnapshot.docs로
모든 문서 객체의 배열을
map함수로 문서의 고유 ID를 바탕으로
문서의 데이터를 객체로 반환해줍니다.
memberList.forEach(member => {
const memberDiv = createMemberElement(member);
memberSection.appendChild(memberDiv);
});
그렇게 만들어진 배열의 각 요소에 대해
반복 작업으로 createMemberElement()함수를 호출하면서
배열의 element를 매개변수로 전달해줍니다.
createMemberElement 함수는
function createMemberElement(member) {
const newMember = document.createElement('div');
newMember.className = 'member';
newMember.innerHTML = `
<div class="member-photo" style="background-image: url('${member.photoUrl}');"></div>
<div class="member-info">
<h3>${member.name} <a href="${member.githubUrl}" target="_blank" class="github-button">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.54 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.13 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.11.16 1.93.08 2.13.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.19 0 .21.15.46.55.38A8.002 8.002 0 0016 8c0-4.42-3.58-8-8-8z"></path>
</svg>
</a></h3>
<p>${member.description}</p>
<p class="mbti">MBTI: ${member.mbti}</p>
</div>
<button class="member-button" onclick="openModal('${member.name}')">Button</button>
<button class="delete-member-button" onclick="deleteMember('${member.id}')">X</button>
`;
return newMember;
}
위와 같은 구성으로 이뤄져 있고,
const newMember = document.createElement('div');
로 HTML에 새로운 내용물을 담을 틀을 만들어줍니다. 그 후,
newMember.className = 'member';
새롭게 만들어진 틀에
newMember.className = "CSS Class 이름"
적용할 CSS(꾸미기와 크기)
Class 이름을 지정해줍니다.
newMember.innerHTML =
`
<div class="member-photo" style="background-image: url('${member.photoUrl}');"></div>
<div class="member-info">
<h3>${member.name} <a href="${member.githubUrl}" target="_blank" class="github-button">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.54 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.13 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.11.16 1.93.08 2.13.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.19 0 .21.15.46.55.38A8.002 8.002 0 0016 8c0-4.42-3.58-8-8-8z"></path>
</svg>
</a></h3>
<p>${member.description}</p>
<p class="mbti">MBTI: ${member.mbti}</p>
</div>
<button class="member-button" onclick="openModal('${member.name}')">Button</button>
<button class="delete-member-button" onclick="deleteMember('${member.id}')">X</button>
`;
newMember.innerHTML과 `(1번 옆 버튼) HTML 테그 문법 ` (1번 옆 버튼)
으로 HTML을 작성해줍니다. 이때,
${배열Element.Value}는 변화가 있는 변수를
표현한 문법 입니다.
(HTML에서 입력된 값은 바뀌는 값들이라 사용)
그렇게 만들어진 newMember 변수를
loadMembers 함수의
memberDiv에 반환시켜줍니다.
memberSection.appendChild(memberDiv);
memberSection에 자식 요소로
memberDiv를 추가해줍니다.
그렇게 만들어진 "팀원 카드"
그 후, 사소한 부분이지만
closeAddModeal()함수를 호출해
export function closeAddModal() {
var addModal = document.getElementById("add-modal");
addModal.style.display = "none";
}
<div id="add-modal" class="modal">
모달의 테그들은 편의상 제거 상태
</div>
모달의 ID를 가져오고
addModal.style.display = "none"
으로 주어 모달이 안 보이게 해줍니다.
이제 "맴버 카드"에서 "Button"과 "X" 버튼에 따른 동작을 설명할 것인데,
"X"버튼을 먼저 클릭하면
<button class="delete-member-button" onclick="deleteMember('${member.id}')">X</button>
onclick으로 deleteMember함수가 호출되는데
이때, ('${member.id}')로 삭제할 대상의
맴버 카드를 삭제 해줍니다.
deleteMember 함수는
export async function deleteMember(id) {
const memberDoc = doc(db, ' 컬렉션 이름', id);
await deleteDoc(memberDoc);
loadMembers();
}
위와 같이 작성 돼 있으며,
const memberDoc = doc(db, '컬렉션 이름', id);
doc(import 받은 기능)으로 "컬렉션 이름"에
전달 받은 id값에 해당하는 자료를 가져옵니다.
그리고
await deleteDoc(memberDoc);
await deleteDoc(memberDoc)로
deleteDoc(import 받은 기능) : 비동기 함수로
FireBase DB에서 특정 문서를 삭제합니다.
await : 비동기 함수 앞에 사용되며,
비동기 함수가 동작 하는데 걸리는 시간 동안
그 다음 코드들이 동작하지 않게 만들어
코드의 가독성과 순차적 실행을 보장해줍니다.
loadMembers 함수는 삭제된 맴버 카드가 생겼기 때문에
새로고침 개념으로 실행시켜
삭제돈 맴버 카드가 보이지 않게 해줍니다.
(위에 설명 있습니다.)
다음으로,
"Button" 클릭 시
<button class="member-button" onclick="openModal('${member.name}')">Button</button>
onclick을 통해 openModal함수가 호출되고,
${member.name} 해당 맴버의 이름이
함수에 전달 됩니다.
export async function openModal(name) {
const membersCol = collection(db, '컬렉션 이름');
const q = query(membersCol, where('name', '==', name));
const membersSnapshot = await getDocs(q);
if (!membersSnapshot.empty) {
const member = membersSnapshot.docs[0].data();
document.getElementById('modal-name').innerText = member.name;
document.getElementById('modal-description').value = member.description;
document.getElementById('modal-strength').value = member.strength || '';
document.getElementById('modal-style').value = member.style || '';
document.getElementById('modal-photo').style.backgroundImage = `url(${member.photoUrl})`;
var modal = document.getElementById("modal");
var modalContent = modal.querySelector(".modal-content");
modal.style.display = "block";
modal.classList.remove("hide");
modalContent.classList.remove("hide");
}
}
openModal은 위와 같이 구성돼 있으며,
const membersCol = collection(db, '컬렉션 이름' );
로 DB에 있는 "컬렉션"을 가져옵니다.
(collection은 import 받은 함수)
const q = query(membersCol, where('name', '==', name));
가져온 컬렉션을 query(import 받은 함수)
함수로 DataBase의 데이터를 검색합니다.
검색 방법은
where(import 받은 함수)로 검색을 하는데
where의 매개변수로는
('필드 이름', '연산 조건', '비교할 값)가 있습니다.
연산 조건에는
비교 연산자들 전부, in, not in, array-contains가 있습니다.
비교 연산자들 전부 , in, not in, array-contains에
부합하는 쿼리 객체가
const q에 담기게 됩니다.
그렇게 담긴 query는
const membersSnapshot = await getDocs(q);
비동기 함수(getDosc(import 받은 함수))
query에 담긴 문서들을 비동기적으로 가져와
const membersSnapshot 변수에 담습니다.
(문서들을 읽고 처리할 수 있게 만들어준다.)
if (!membersSnapshot.empty) {
const member = membersSnapshot.docs[0].data();
document.getElementById('modal-name').innerText = member.name;
document.getElementById('modal-description').value = member.description;
document.getElementById('modal-strength').value = member.strength || '';
document.getElementById('modal-style').value = member.style || '';
document.getElementById('modal-photo').style.backgroundImage = `url(${member.photoUrl})`;
var modal = document.getElementById("modal");
var modalContent = modal.querySelector(".modal-content");
modal.style.display = "block";
modal.classList.remove("hide");
modalContent.classList.remove("hide");
}
if(!membersSnapshot.empty)
가져온 문서들의 스냅샷을
포함하는 객체가
공백이 아닐경우 = 내용물이 있을 경우
(앞에 !(느낌표)(부정)이 있습니다)
const memberDoc = membersSnapshot.docs[0];
첫 번 째 스냅샷의 문서를 가져옵니다.
(
사실 동명 2인이 있을 수 있기 때문에
이름으로 찾고자 하는 스냅들 중
primary key로 진짜 찾고자 하는 사람을
찾아야 하지만 현재는 스냅들 중
첫 번째 문서(가장 앞에 있는 문서)를 가져온다.
그렇게 가져온 사람의 정보를
document.getElementById('modal-name').innerText = member.name;
document.getElementById('modal-description').value = member.description;
document.getElementById('modal-strength').value = member.strength || '';
document.getElementById('modal-style').value = member.style || '';
document.getElementById('modal-photo').style.backgroundImage = `url(${member.photoUrl})`;
document,getElementById('가져올 HTML 태그의 ID').innerText나 value로
JavaScript에서 HTML 태그 중 해당하는 Id로 해당 HTML 태그를 가져와
해당 사람의 이름, 소개, 강점(desription), 스타일(style), 이미지를 HTML에 집어 넣는다.
(
만약, 소개(desription), 강점(style)의 내용이 없을 경우
(undefined, null, 0, false, NaN, 또는 빈 문자열)
''(공백)을 반환 합니다.
)
이후,
var modal = document.getElementById("modal");
로 document.getElementById("modal")로 HTML에서 Id가 "modal"인
HTML 태그 부분을 가져옵니다.
HTML에 Id가 "modal"인 태그는
<div id="modal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeModal()">×</span>
<div class="modal-inner-content">
<div class="modal-photo" id="modal-photo">사진</div>
<div class="modal-info">
<h3 id="modal-name"></h3>
<div class="modal-description">
<label>설명</label>
<textarea id="modal-description"></textarea>
</div>
<div class="modal-strength">
<label>나의 장점</label>
<textarea id="modal-strength"></textarea>
</div>
<div class="modal-style">
<label>협업 스타일</label>
<textarea id="modal-style"></textarea>
</div>
<div class="modal-buttons">
<button class="save-button" onclick="saveMemberData()">등록</button>
<button class="cancel-button" onclick="closeModal()">취소</button>
</div>
</div>
</div>
</div>
</div>
입니다.
그 후,
var modalContent = modal.querySelector(".modal-content");
로 modal 내부 HTML 태그 중 Id가
modal-content인 HTML 태그를
가져온다.
modal.style.display = "block";
style.display = "block"으로
블록 형식으로 창이
뜰 수 있게 만들어줍니다.
modal.classList.remove("hide");
modalContent.classList.remove("hide");
로 modal과 modalContent의 CSS 중
hide class를 제거해줍니다.
그 결과,
Web에서는 위와 같은 모달이 생성 됩니다.
모달 내부 "나의 장점", "협업 스타일"을 작성하고
"등록" 버튼을 클릭하면
HTML 멤버 등록 버튼의
<button class="save-button" onclick="saveMemberData()">등록</button>
onclick에 의해 saveMemberData 함수가 호출 됩니다.
export async function saveMemberData() {
const name = document.getElementById('modal-name').innerText;
const description = document.getElementById('modal-description').value;
const strength = document.getElementById('modal-strength').value;
const style = document.getElementById('modal-style').value;
const membersCol = collection(db, 'JinYunSeTestDB');
const q = query(membersCol, where('name', '==', name));
const membersSnapshot = await getDocs(q);
if (!membersSnapshot.empty) {
const memberDoc = membersSnapshot.docs[0];
await updateDoc(memberDoc.ref, {
description: description,
strength: strength,
style: style
});
loadMembers();
closeModal();
}
}
(
위에 유사한 내용들이 있어서
간략하게 작성했습니다.
)
saveMemberData() 함수에서는
이름, 소개, 강점, 스타일에
해당하는 HTML Id를
바탕으로 JavaScript 함수에서
입력된 값들을 변수에 저장한 후
사용할 DB의 컬렉션을 가져와
컬렉션 조건절에 부합하는
query를 변수에 저장하고
query를 바탕으로 스냅샷을 가져와
스냅샷이 비어있지 않다면
(
위에 유사한 내용들이 있어서
간략하게 작성했습니다.
/)
if (!membersSnapshot.empty) {
const memberDoc = membersSnapshot.docs[0];
await updateDoc(memberDoc.ref, {
description: description,
strength: strength,
style: style
});
loadMembers();
closeModal();
}
updateDoc(import 받은 함수)로
내용을 새롭게 갱신시켜 줍니다.
그 후, loadMembers함수와
closeModal()함수로
맴버 카드가 들어갈 섹션을 갱신시켜주고
모달을 닫습니다.
삭제 버튼을 클릭하면
export async function deleteMember(id) {
const memberDoc = doc(db, 'JinYunSeTestDB', id);
await deleteDoc(memberDoc);
loadMembers();
}
deleteMember 함수 호출로
deleteDoc(import 함수) 로 해당 id의
문서를 삭제 후,
loadMembers함수로
멤버 카드 색션을 갱신시켜줬다.