패스트캠퍼스X야놀자 프론트엔드 개발 부트캠프 개인프로젝트 후기

Jun_Gyu·2023년 8월 28일
0
post-thumbnail

알바생 인적사항 관리 서비스, 우리알바

아르바이트생의 인적사항을 간단하게 관리할 수 있도록 만들어본 프로젝트입니다.

🖱 프로젝트 배포 주소

example

초기 비밀번호는 1234입니다!!

총 개발기간

2023.08.07 ~ 2023.08.16

사용한 스택들

Tool

Development


📺 주요 구현화면 및 기능

1. 웰컴 페이지2. 관리자인증 페이지
imageimage
3. 알바생 등록 페이지4. 알바생 조회 페이지5. 알바생 수정 페이지
imageimageimage


1. 로딩화면 구현

loading

[ 로딩화면 ] 관련 JS코드

로딩 화면은 로딩바가 가로로 늘어나는 애니메이션을 무한히 반복하도록 했고,
로딩이 완료되면 addEventListener를 통해 해당 화면이 사라지도록 했습니다.

<!-- 로딩 화면 추가 -->
    <div class="loading-screen">
      <div class="loading-bar"></div>
    </div>
:root {
  --display: flex;
}

.loading-screen {
  display: var(--display);
  position: fixed;
  justify-content: center;
  align-items: center;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #3da557;
  transition: opacity 0.5s ease;
  z-index: 1000;
}

.loading-bar {
  width: 441px;
  height: 5px;
  background-color: #ffffff;
  animation: loadingBar 2.5s infinite; /* 로딩 바 애니메이션 설정 */
  text-align: center;
}

@keyframes loadingBar {
  0% {
    width: 0; /* 로딩 바 초기 길이 */
  }
  100% {
    width: 100%; /* 로딩 바 최종 길이 */
  }
}
document.addEventListener('DOMContentLoaded', function () {
  setTimeout(() => {
    loadingScreen.style.opacity = '0';
  }, 1000);

  const loadingScreen = document.querySelector('.loading-screen');

  // 투명도 변화 이벤트 감지
  loadingScreen.addEventListener(
    'transitionend',
    (handleTransitionEnd = () => {
      document.documentElement.style.setProperty("--display", "none");
    })
  );
});


2. 암호 유효성 검사 기능

입력된 암호가 없을 시잘못된 암호 입력시
pwck1pwck2

[ 암호 유효성 검사 ] 관련 JS코드


개인적으로 아쉬움이 많이 남았던 부분이었습니다.

위의 기능은 기본적으로 로그인을 한 상태 라는 가정하에 진행된 페이지입니다.

회원가입 및 로그인 기능을 구현하기에는 Firebase 숙련도와 시간적 한계에 부딪혀 본인인증이라는 간단한 형태만 잡아둔 상태입니다.

<!-- 화면 중간 비밀번호 확인용 섹션 -->
    <section class="password-check">
      <h2>매장 관리자 비밀번호 확인</h2>
      <input type="password" id="passwordInput" placeholder="비밀번호 입력"/>
      <button id="loginButton">로그인 하기</button>
      <!-- 비밀번호 오류시 에러메세지 출력 -->
      <div class="error-message" id="errorMessage"></div>
const passwordInput = document.getElementById("passwordInput");
const errorMessage = document.getElementById("errorMessage");

// 유효성 검사 기능 추가
loginButton.addEventListener("click", () => {
  const password = passwordInput.value;
  if (password === "1234") {
    // 로그인 성공 시 직원 관리페이지로 이동
    window.location.href = "myAlba.html";
  } else if (password === "") {
    // 비밀번호 오류 메세지 1 (입력된 값이 없을때)
    errorMessage.textContent = "비밀번호를 입력해주세요!";
    setTimeout(()=>{
      errorMessage.textContent = "";
    }, 3000)
  } else {
    // 비밀번호 오류 메세지 2 (비밀번호가 틀렸을때)
    errorMessage.textContent = "비밀번호를 다시 확인해주세요!";
    setTimeout(()=>{
      errorMessage.textContent = "";
    }, 3000)
  }
});


3. 첨부 이미지 미리보기 기능

imgShow

[ 첨부 이미지 미리보기 ] 관련 JS코드


이미지를 등록할 시 사용자들이 등록한 이미지를 바로 확인할 수 있도록 미리보기 기능을 구현했습니다.
<img> 태그를 추가하여 이미지를 첨부하지 않은 상태에서는 기본 이미지가 출력되도록 하였고,
이미지를 첨부한 경우에는 FileReader 객체를 생성하여 미리보기 이미지의 데이터 URL을 변경합니다.

<div class="image-upload">
              <img id="imagePreview" src="../assets/pictures/no-image.png" alt="아르바이트생 사진" />
              <input type="file" name="image" id="photoInput" accept="image/*" />
            </div>
const photoInput = document.getElementById('photoInput');
photoInput.addEventListener('change', (event) => handleImageChange(event));

// 이미지 등록 시 미리보기 기능
const handleImageChange = (event) => {
  const imagePreview = document.getElementById('imagePreview');
  const selectedImage = event.target.files[0];
  let imageURL = '../assets/pictures/no-image.png';
  
  if (selectedImage) {
    // 이미지 등록 시
    imageURL = URL.createObjectURL(selectedImage);
    imagePreview.src = imageURL; // 등록한 파일로 미리보기 주소변경
    imagePreview.onload = () => {
      URL.revokeObjectURL(imageURL); // 이미지가 로드되면 URL 해제
    };
  } 
};


4. 데이터 조회 및 프로필 모달창

데스크탑 화면모바일 화면
profile_desktopprofile_mobile

[ 프로필 모달창 ] 관련 JS코드


모달창 기능을 구현하는 도중, 누르려는 버튼의 행에 있는 데이터를 가져올 때 약간의 귀찮음(?)으로 탄생한 코드입니다.

사실 처음에 별 생각 없이 같은 행의 값들 중 이름값을 불러와서
모달창을 구현하려고 하다보니, 등록된 데이터들 중 동명이인이 발생하게 되면 한사람의 프로필만 표시되는 오류가 발생했었습니다.

원래대로라면 DB에 저장된 데이터의 ID값을 불러온 이후, 해당 데이터의 속성값들을 modal창에 띄우는 방식을 사용해야겠지만,
'굳이 추가적으로 ID 값을 불러오지 않고서도 현재 부모 행에 존재하는 전화번호를 이용하면 되지 않을까' 라는 생각에
DB에 저장된 값들 중 전화번호가 일치하는 값을 들고와서 modal창에 띄우는 방식을 사용했습니다.
(전화번호는 지구상에 모든 사람들이 각기 다른 번호를 가지고 있으니까요!)

지금 PR을 작성하는 현재로써 돌이켜보면 단순한 편법이었다고 생각합니다..

// '조회하기' 버튼 클릭 이벤트 리스너 등록
$(document).on('click', '.edit-button', function () {
  // 클릭한 버튼의 부모 요소에서 데이터 가져오기
  const $parentRow = $(this).closest('tr');
  const phoneNum = $parentRow.find('.alba-phone p').text(); // <--- 이부분. 사실 DB에 저장된 id 값으로 받아오는게 원래 올바른 방법이긴 하다.. ㅎㅎ 

  // 데이터베이스에서 해당하는 데이터 가져오기
  // '조회하기' 버튼 클릭 이벤트 리스너 등록
$(document).on('click', '.edit-button', function () {
  // 클릭한 버튼의 부모 요소에서 데이터 가져오기
  const $parentRow = $(this).closest('tr');
  const phoneNum = $parentRow.find('.alba-phone p').text();

  // 데이터베이스에서 해당하는 데이터 가져오기
  db.collection('albainfo')
    .where('연락처', '==', phoneNum)
    .get()
    .then((querySnapshot) => {
      if (querySnapshot.empty) {
        // 데이터가 없을경우 리턴.
        return;
      }
      const doc = querySnapshot.docs[0];
      const data = doc.data();
      // 모달창에 데이터 채우기
      document.querySelector('.modal-left-container img').src = data.이미지;
      document.querySelectorAll('.modal-right-container p')[0].textContent = data.이름;
      document.querySelectorAll('.modal-right-container p')[1].textContent = data.직급;
      document.querySelectorAll('.modal-right-container p')[2].textContent = data.연락처;
      document.querySelectorAll('.modal-right-container p')[3].textContent = data.근무시간;
      // 모달창 띄우기
      $('.modal-container').fadeIn();

      // 모달창 버튼 클릭 이벤트 리스너 등록
      $('.update-button').on('click', function () {
        // 정보수정 이동
        const id = doc.id;
        window.location.href = `albaUpdate.html?idvalue=${id}`;
        $('.modal-container').fadeOut();});
      $('.close-button').on('click', function () {
        // 모달창 닫기
        $('.modal-container').fadeOut();
      });
    })
    .catch((error) => {
      console.error('Error getting document:', error);
    });
});


5. 데이터 수정 기능

profile_update

[ 데이터 수정 ] 관련 JS코드


'Restful.api에서 사용하는 방식처럼 url 끝에 조회하고자 하는 데이터의 id값을 넣어서 보내면 되겠다' 라는 아이디어로 시작하여
URL 끝에 id값을 포함하여 수정하고자 하는 데이터를 불러올 수 있도록 했습니다.

추가적으로 반복하여 db, storage를 불러오는 코드는 firebaseSDK.js파일에 저장하여 import해오는 방식으로 코드의 반복을 최소한으로 했습니다.

// Firebase SDK
import { initializeFirebase } from './firebaseSDK.js';
const { db, storage } = initializeFirebase();

$(document).ready(function () {
  const urlParams = new URLSearchParams(window.location.search);
  const idValue = urlParams.get('idvalue');

  if (idValue) {
    const docRef = db.collection('albainfo').doc(idValue);

    docRef
      .get()
      .then((doc) => {
        if (doc.exists) {
          const data = doc.data();
          // 데이터를 이용하여 폼 필드에 값을 설정하거나 화면에 표시
          $('#imagePreview').attr('src', data.이미지);
          $('#name').val(data.이름);
          $('#phone').val(data.연락처);
          $('#position').val(data.직급);
          $('#workingHours').val(data.근무시간);
        } else {
          console.error('오류발생! 데이터를 불러올 수 없음!');
        }
      })
      .catch((error) => {
        console.log('Error getting document:', error);
      });

    // 수정 버튼 클릭 시 데이터 수정
    $('#sendButton').click(function () {
      const updatedData = {
        이름: $('#name').val(),
        연락처: $('#phone').val(),
        직급: $('#position').val(),
        근무시간: $('#workingHours').val(),
        // 기타 필요한 필드 추가
      };

      // 이미지 업로드 처리
      const selectedImage = $('#photoInput')[0].files[0];

      // 데이터 업로드 함수
      const dataUpload = () => {
        docRef
          .update(updatedData)
          .then(() => {
            console.log('문서 업데이트 완료');
            window.location.href = '/albaSelect.html';
          })
          .catch((error) => {
            console.error('오류 발생:', error);
          });
      };

      if (selectedImage) {
        const storageRef = storage.ref(`images/${selectedImage.name}`);
        storageRef
          .put(selectedImage)
          .then((snapshot) => {
            return snapshot.ref.getDownloadURL();
          })
          .then((downloadURL) => {
            // 업로드된 이미지 URL을 데이터와 함께 업데이트
            updatedData.이미지 = downloadURL;
            dataUpload();
          })
          .catch((error) => {
            console.error('이미지 업로드 오류:', error);
          });
      } else {
        // 이미지가 선택되지 않은 경우 데이터만 업데이트
        dataUpload();
      }
    });


6. 데이터 선택 후 일괄삭제 기능

image

[ 일괄삭제 ] 관련 JS코드


해당 부분에서는 삭제하고자 하는 알바생들의 정보를 체크박스를 통해 다중선택하여 지울 수 있도록 구현했습니다.

// 알바생 데이터 삭제
$(document).ready(function () {
  // 삭제 버튼 누르기
  $(document).on('click', '.alba-delete-button', function () {
    // 선택한 알바생 데이터의 id값 추출하여 배열에 저장
    const selectedIds = [];

    // 체크박스 확인
    $('.alba-list-body input[type="checkbox"]:checked').each(async function () {
      const $parentRow = $(this).closest('tr');
      const phoneNum = $parentRow.find('.alba-phone p').text();
      const collection = db.collection('albainfo');
      const docRef = collection.where('연락처', '==', phoneNum);
      const querySnapshot = await docRef.get();
      const storage = firebase.storage();

      if (querySnapshot.size > 0) {
        const doc = querySnapshot.docs[0];
        const id = doc.id;
        selectedIds.push(id);
      }

      const deleteButton = $('.alba-delete-button');
      if (selectedIds.length > 0) {
        deleteButton.removeClass('disabled-button').prop('disabled', false);
      } else {
        deleteButton.addClass('disabled-button').prop('disabled', true);
      }

      if (selectedIds.length > 0) {
        console.log(123);
        $('.confirm-modal-container').fadeIn();
        // 삭제하기 버튼 클릭 이벤트 리스너 등록
        $('.confirm-button').on('click', async function () {
          // 선택한 알바생 데이터 삭제 처리
          const deletePromises = selectedIds.map(async (id) => {
            // Firestore에서 해당 데이터 가져오기
            const docRef = db.collection('albainfo').doc(id);
            const docRefData = await docRef.get();

            // Firestore 문서에 저장된 이미지 URL주소 가져오기
            const imageData = docRefData.data();
            const imageUrl = imageData.이미지; // 속성값에서 이미지 파일 URL 추출

            // Firestore DB에서 데이터 삭제
            await docRef.delete();
            
            // Storage에서 이미지 삭제
            if (imageUrl) {
              const storageRef = storage.refFromURL(imageUrl);
              await storageRef.delete();
            }
          });
          // 삭제 Promise 모두 완료될 때까지 기다림
          await Promise.all(deletePromises);
          window.location.href = 'albaSelect.html'; // 새로고침 기능
        });
      } else {
        $('.no-selection-modal-container').fadeIn();
        return;
      }
    });
  });
});


사용한 폰트


🖱 User Flow Diagram

user_flow


💦 그리고 리팩토링..!

멘토님께서 수많은 코드들 중에서도 유독 제가 취약한 부분에 대해서 자세하게 짚어주고 가셨는데, 특히나 지난번 코드리뷰보다도 좀 더 코드의 작성 요령에 대해서 많은것들을 배울 수 있었습니다.

정말 많은 부분에서 코드리뷰를 진행해주셨는데, 그중에서도 특히 윗부분의 코드리뷰가 정말 큰 도움이 되었다고 생각합니다.

코드를 수정하는 과정에서 앞서 멘토님께서 설명해주신 다른 부분도 연이어 적용되는 부분에 대해서 조금 더 깊이 이해할 수 있게 되었고, 미처 발견하지 못했던 다른 부분의 오류사항도 수정할 수 있었던 좋은 기회였지 않나 싶습니다.

새롭게 알게된 점

  • DOM의 속성에도 점 표기법으로 접근이 가능함! ===(getAttribute)
  • 네이밍 규칙성의 존재
  • 변수를 활용한 CSS 스타일 관리

부족했던 점

  • 네이밍 규칙성을 어기고 명칭을 마구잡이로 작성했던 점.
  • 모바일, 테블릿, 데스크탑의 체계적인 반응형을 구축하지 못한 점.
  • 바닐라 JS에 아직 익숙치 않아 jQuery의 힘을 다수 빌린 점.
  • 반복되는 코드들의 중복성 배제에 대해 다소 부족했던 점.
profile
시작은 미약하지만, 그 끝은 창대하리라

0개의 댓글