-
[3일차] 미니프로젝트 완료 및 피드백 반영내일배움캠프/미니프로젝트 2025. 2. 21. 12:20
✅ 오늘의 목표
- 디자인 통일
- 피드백 반영
- 기능 개선
✅ 디자인 통일
메인페이지 작업자 분께서 이렇게 예쁘게 메인을 디자인해주셨다.
멤버 관리의 기존 디자인을
아래와 같이 통일 시켜주었다.
통일감을 주니 훨씬 보기좋아졌다.
✅ 피드백 반영
header fix, 메인화면 소제목
튜터님께 피드백 받은 내용이다.
내가 한 번 해보기로 하였다.
1. Header Fix
처음에는 Header 전체를 Fix 했었다.
해당 작업을 한 후 팀원에게 이미지는 올라가고 Menu Nav 만 고정되면 어떨까라는 피드백을 받았다.
그게 확실히 더 예쁠 것 같았다.
구글링을 통해 방법을 찾아보았는데
css 에서
position: sticky;
를 사용하면 된다는 것을 알았다.
sticky position의 동작 방식
- 브라우저가 처음 랜더링 되었을 때 sticky 요소는 문서 흐름에 따라 일반적인 위치에 배치됩니다.
- 스크롤이 지정된 위치에 도달할 때까지 sticky 요소는 일반적인 흐름대로 따라 이동합니다.
- 스크롤의 좌표가 지정된 위치에 도달하면 sticky 요소가 지정된 위치에 고정됩니다.
- 이제 지정된 위치 이하로 스크롤해도 해당 위치에 고정되어 유지됩니다.
참조: https://coding-factory.tistory.com/951
이렇게 menu 에 position: sticky; css를 적용했는데 아래와 같이 적용이 되질 않았다.
그래서 문제가 무엇인지 한참을 살펴보고, sticky position은 부모 요소 안에서만 고정된다는 것을 알았다.
$(function () { $("#header").load("common/header.html"); $("#footer").load("common/footer.html"); });
우리는 해당 방식으로 header와 footer 를 load하고 있어서
<body> <header> <div class="member-list"> <img src="./assets/images/green_resized.png" alt="Member 1"> <img src="/assets/images/red_resized.png" alt="Member 2"> <img src="/assets/images/yellow_resized.png" alt="Member 3"> <img src="/assets/images/white_resized.png" alt="Member 4"> <img src="/assets/images/black_resized.png" alt="Member 5"> </div> <nav class="menu"> <a href="/pages/main.html">INTRO</a> <a href="/pages/main.html">HELMET</a> <a href="/pages/main.html">MEMBER CARD</a> <a href="/pages/guestBook.html">방명록</a> </nav> </header> </body>
menu nav의 부모가 header였다. sticky 를 하려면 body를 부모로 지정해야했다.
그래서 선택한 방법은 header가 load 된 후 header 에서 nav 를 빼서 header 밑에 배치시키는 방법을 선택했다. (야매)
$("#header").load("common/header.html", function () { const menu = $("#header .menu").detach(); $("#header").after(menu); }); $("#footer").load("common/footer.html");
이렇게 수정 후 확인을 해보니
nav sticky 가 정상적으로 작동하는 것을 확인할 수 있었다!
position: sticky; top: 0; z-index: 1000;
그리고 nav를 최상단에 고정하기 위해 top: 0;을 주었고 맨 앞에 배치하기 위해 z-index 를 1000주었다.
2. 메인페이지 구역별 소제목
메인 소개부분에 구역별로 소제목이 있어서 구분감을 줬으면 좋겠다는 피드백이었다.
그래서 구역별로 소제목을 주고 margin 을 조금 더 줘서 해당 구역을 구분감을 더 주었다.
✅ 기능 개선
1. 이미지 서버 저장
기존에는 멤버를 등록할 때 이미지를 저장하는게 아니라, 이미지를 FileReader를 사용해서 Base64로 인코딩해서 그 결과값을 저장하는 형식으로 했었다.
const reader = new FileReader() reader.onload = (e) => { img.src = e.target.result } reader.readAsDataURL(file)
다른 분들의 코드를 보다가 이미지를 Firebase의 CloudStorage 사용하셔서 작성하신 것을 보았다.CloudStorage 를 사용하려면 Blaze 요금제를 사용해야한다고해서 고민했는데, 5GB/월 까지 무료로 사용가능하다해서 적용해보았다.
이미지를 업로드 했을 때 사진을 서버로 전송하면 더미데이터가 쌓일 것을 고려하여 미리보기는 똑같이 base64로 인코딩해서 보여주었고 저장할 때 서버로 전송하는 방식을 택했다.
const storageRef = ref(storage, `memberImages/${img.name}`) await uploadBytes(storageRef, img) const imgUrl = await getDownloadURL(storageRef)
storageRef에 어느 경로에 파일을 저장할 것인지 선택해준 뒤 uploadBytes로 파일을 업로드해준다.
그 뒤 getDownloadURL을 사용하여 업로드 된 파일의 url 을 return 받아서 서버에 저장해주었다.
해당 함수들을 사용하여 서버로 이미지 전송을 하니 저장 시에 시간이 조금 걸렸다.
그래서 사용자들이 지금 저장 중인건지 잘 모를 수 있을 것 같아서 저장 중인지 알 수 있도록 스피너를 구성하였다.
저장이 시작되면 해당 스피너가 화면에 표시되고 완료시 alert 와 함께 스피너가 사라지도록 하여, 사용자가 저장 중인지 직관적으로 알 수 있게 되었다.
2. Header 클릭 시 구역별로 이동
페이지 내 특정 영역으로 스크롤 이동하도록 만들려면, header 에 각 영역에 고유한 id를 부여하고, header 메뉴 링크에서 해당 id를 참조하는 앵커 링크(#)로 연결하는 방식을 사용했다.
각 구역별로 고유 id를 부여한다음
function scrollToHashElement() { if (location.hash) { const target = document.querySelector(location.hash); if (target) { const headerOffset = 60; const elementPosition = target.getBoundingClientRect().top; const offsetPosition = elementPosition + window.scrollY - headerOffset; window.scrollTo({ top: offsetPosition, behavior: "smooth" }); } } }
scroll-behavior을 이용한 함수를 작성하여 조금 더 세밀한 이동이 가능하도록 구현을 하였다.
화면이 샤샤샥 움직이는 것을 볼 수 있다.
3. 구역별로 이동시 header active 효과 IntersectionObserver 함수.
메뉴에 hover 시에는 색이 변하는데 화면을 이동하면 똑같이 active가 되면 좋겠다고 생각을 했다.
<script> // 감시할 섹션들을 선택 (각 섹션에 고유한 id가 있어야 함) const sections = document.querySelectorAll('#intro, #helmet, #memberCard'); // Intersection Observer 옵션 (50% 이상 보이면 active로 처리) const options = { root: null, rootMargin: '0px', threshold: 0.5 }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const id = entry.target.getAttribute('id'); // 모든 네비게이션 링크의 active 제거 document.querySelectorAll('.menu a').forEach(link => { link.classList.remove('active'); // href에 해당 id가 포함되면 active 추가 if (link.getAttribute('href').includes('#' + id)) { link.classList.add('active'); } }); } }); }, options); // 각 섹션 관찰 시작 sections.forEach(section => { observer.observe(section); }); </script>
InterserctionObserver 함수를 사용하여 해당 구역들을 감시하도록 구성하여 구역의 50%가 보이면 기존 active를 제거하고 해당 menu를 찾아서 active하도록 구성해주었다.
페이지 이동시 nav가 active 되는 것을 볼 수 있다. 굿굿
4. 페이지 깜빡임 현상 개선
페이지가 header, footer 를 load 하고 또 그 후 css도 적용되고 그래서인지 화면이 구성될 때 깜빡임 현상이 있었다.
그래서 해결한 방법이
<style> body { visibility: hidden; } </style>
body style에 visibillity: hidden; 으로 처음 페이지가 열릴 때 화면을 숨겨주었고,
<script> $(function () { $("#header").load("common/header.html", function () { const menu = $("#header .menu").detach(); $("#header").after(menu); $("#footer").load("common/footer.html", function () { $("body").css("visibility", "visible"); }); }); }); </script>
header load후 footer load를 하고 body visibillity를 visible로 변경하여 화면을 볼 수 있도록 하였다.
그 후에는 깜빡임 현상이 사라졌다.
✅ 전체 페이지
1. Main Page
2. Member Detail
3.Guest Book
4. Admin Member List
5.Admin Member Create
이것으로 모든 페이지 작업이 완성 되었다. 팀원분들이 너무 다들 잘해주셔서 편하게 작업했던 것 같다.
내일은 발표 자료 제작을 하고 개선사항이 없나 조금 더 볼 것 같다.
'내일배움캠프 > 미니프로젝트' 카테고리의 다른 글
[5일차] 프로젝트 회고 (2) 2025.02.24 [4일차] 프로젝트 마무리 (1) 2025.02.24 [2일차] 미니프로젝트 진행 (Firebase) (1) 2025.02.20 [1일차] 개강 OT & 미니 프로젝트 (0) 2025.02.20