첫 협업! 프로젝트 회고와 앞으로의 방향

J.A.Y·2024년 1월 28일
0

프로젝트

목록 보기
2/5

AOO(Atomospheric and Oceanographic Organization) 대기와 해양 보호 기구

링크: https://aoo-team3-project1.netlify.app/
위 URL에서 확인하실 수 있습니다.

환경 보호 기구 웹사이트를 만들게 된 이유

첫 협업 프로젝트로 어떤 주제를 정할지 팀원과 상의했었습니다. 다들 처음이기에 개발하고 싶은 것은 많았던 반면, 주제는 딱히 떠오르지 않아 어려움을 겪었습니다. 그러다 팀원 중 한 분이 최근 북극곰과 관련된 환경 다큐에 대해 이야기를 꺼냈고, 이를 들은 나도 기후 변화와 우리나라 해수면 상승과 관련된 기사를 본 것을 이야기하면서 점차 '환경'이슈를 다룰 수 있는 웹이면 좋겠다는 의견으로 좁혀져 갔습니다. 그래서 내린 결론은 바로...

"개발자로서 환경 오염에 대한 경각심을 널리 알려 사회 공헌할 수 있는 사이트를 만들자!"

만들기 전이라 가능했던 생각... 비록 의지와 달리 아직 부족한 점들이 많이 있지만 , 언젠가는 실현할 수 있는 개발자가 되겠습니다.

사용한 기술과 어플리케이션

  • FE: Html + CSS + JS(바닐라JS+JQeury+JSON)
  • 소통 수단: slack, kakaotalk
  • API: Yotubue, Google Map
  • 회의록: Notion(1차, 2차, 3차)
  • 자료 제작 또는 검색: Pixababy, Google Font, Canva
  • 역할

  • 팀원1: 소개, 대기 사업, dakr&light mode
  • 팀원2: 후원, 후원정보조회, 해양 사업
  • 나: Header&Footer, 메인, 진행중인 캠페인, 완료된 캠페인, 자주 묻는 질문, 공지사항
  • 원래 페이지 개수/3 해 나름 균등하게 배분하고자 했지만, 개인 역량에 따라 작업 중간에 역할을 조정했었습니다.

    개발 과정


    기능 구현 설명

    Header

    여러 사이트들을 참고해보니 대부분 header가 스크롤 창이 내려가도 위에 항상 고정되어 있는 것을 발견했습니다.

    아무래도 유저가 언제든 원하는 메뉴로 이동할 수 있도록 하기 위해서이지 않을까 싶었습니다.

    그래서 저 또한 position: fixtop: 0을 줘서 스크롤을 내려도 헤더가 화면 상단에 계속 고정되게 만들었습니다.

    ul태그와 li태그로 상위 메뉴 안에 하위 메뉴들을 묶어 각각의 하위 메뉴가 상위 메뉴 바로 하단에 위치되도록 만들었습니다. 이 방식을 이용하면 나중에 css에서 text-align = centerpadding값을 줬을 떼 상위 메뉴와 하위 메뉴가 똑같이 중앙 정렬되고, 여백 간격도 동일하게 적용돼서 css작업이 수월해집니다.

    마지막으로는 각 상위 메뉴에 마우스를 가져다 대면 하위 메뉴들이 뜨도록 JQuery를 적용해줬습니다.
    다만 모바일에서는 마우스가 아닌 터치가 되기 때문에 화면 넓이가 특정 숫자 아래로 줄어들면 해당 기능이 활성화되지 않도록 했습니다.

    $(document).ready(function () {
        // 미디어 쿼리 객체 생성
        const mediaQuery = window.matchMedia("(min-width: 601px)");
    
        // 화면 크기에 따라 이벤트 활성화 또는 비활성화
        function handleMediaQuery() {
            if (mediaQuery.matches) {
                // 화면 크기가 601px 이상인 경우
                $(".introMenu a").mouseover(function () {
                    $(".introSubMenu ul li").css({
                        visibility: "visible",
                        opacity: "1",
                        transition: "1s",
                        top: "2vh",
                    });
                });
            } else {
                // 화면 크기가 600px 이하인 경우
                $(".introMenu a").off("mouseover"); // 이벤트 제거
                $(".activityMenu a").off("mouseover");
                $(".businessMenu a").off("mouseover");
                $(".communicationMenu a").off("mouseover");
                $(".donationMenu a").off("mouseover");
            }
        }
          // 초기 상태 설정
        handleMediaQuery();
    
        // 미디어 쿼리 리스너 등록
        mediaQuery.addListener(handleMediaQuery);
    });

    홈(1): 영상을 활용한 배경 처리

    Youtube iframe API 자세한 내용은 아래 링크를 참고해주시길 바랍니다.
    https://developers.google.com/youtube/iframe_api_reference?hl=ko

    홈페이지를 들어왔을때 '환경'과 관련된 사이트임을 나름 임팩트 있게 전달하고자 사진 대신 영상을 넣어줬습니다. 초반에 Youtube iframe API 코드를 해석하기까지 시간이 다소 오래걸렸는데, 코드 한 줄씩 이해하고 정리하고 나니 적용하는 건 생각보다 쉬웠습니다.

    아래에 API 내부 코드가 각각 어떤 뜻이고, 이 프로젝트에서 사용한 player value는 무엇인지 정리해봤습니다.

    <!DOCTYPE html>
    <html>
      <body>
         <!-- 태그는 무조건 div로 -->
        <div id="player"></div> 
      </body>
    </html>
    var tag = document.createElement("script");
    
    // 스크립트 태그 소스에 유튜브 영상을 재생할 수 있는 자바스크립트 명령어 할당
    tag.src = "https://www.youtube.com/iframe_api";
    // script 태그 중 첫 번째 script태그를 firstScriptTag에 할당
    var firstScriptTag = document.getElementsByTagName("script")[0];
    // 첫 번쨰 스크립트 태그 이전에 youtube API가 실행될 수 있도록 지정
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    
    // youtube 라이브러리에서 함수를 자동으로 찾을 수 있게 Youtube에서 지정해놓음
    // 따라서 함수 이름은 고정된 것
    function onYouTubeIframeAPIReady() {
        new YT.Player("player", {
            videoId: "p9RF9_fsZqg", //최초 재생할 유튜브 영상 ID
            playerVars: { //플레이어 속성들
                autoplay: 1,
                loop: 1, //반복재생여부(1:반복재생 함)
                playlist: "p9RF9_fsZqg", //반복 재생할 유튜브 영상 ID 목록
                rel: 0, //연관 동영상 표시여부(0:표시 안함)
                controls: 0, //플레이어 컨트롤러 표시여부(0:표시 안함)
                playsinline: 1,
                mute: 1, //음소거여부 (1:음소거 함)
            },
            events: {
                onReady: function (event) {
                    // onReady는 함수 event가 실행될때 player와 연결시켜주는 매소드
                    event.target.playVideo()
                },
            },
        });
    }

    홈(2): 후원 바로가기 배너 (jQuery)

    후원 바로가기 배너는 Header를 만드는 방법과 거의 동일합니다. Container를 만든 뒤에 position 값은 fix로, 그런 뒤에 topright 각각에 값을 할당하면 웹 상단 오른쪽에서 조금 떨어진 곳에 배너가 계속 고정이 됩니다.

    하지만 계속 이렇게 고정되어 있으면 유저가 사이트를 이용할 때 불편함을 겪을 수 있어 일부러 화면의 기본 레이아웃 바깥 부분에 배치를 했고, 필요에 따라 X버튼을 클릭하면 사라지는 기능도 추가해줬습니다.

    캠페인: Swiper1 & 2 (jQuery)

    Swiper API 자세한 내용은 아래 링크를 참고해주세요.
    https://swiperjs.com/swiper-api

    캠페인 화면은 진행중인 캠페인 홍보 및 참여를 유도하는 페이지로 긍정적인 리뷰 목록 > 캠페인 목록 순으로 화면에 나오게끔 했습니다.

    처음에 swiper API 내부 코드에 저장되어 있는 변수명을 그대로 사용해야 하는 줄 알고 단일 페이지에 두 개의 swiper을 어떻게 구현해야 좋을지 고민됐었습니다.

    하지만 이런 저런 시도를 해보니 sipwer뒤에 숫자를 적어주면 단일 페이지에서도 swiper을 여러개 넣을 수 있다는 사실을 알게 됐습니다.

    예시

    <div class="swiper1">
      <div class="swiper-wrapper">
        <div class="swiper-slide">Slide 1</div>
        <div class="swiper-slide">Slide 2</div>
        <div class="swiper-slide">Slide 3</div>
      </div>
      <div class="swiper-scrollbar"></div>
    </div>
    <div class="swiper2">
      <div class="swiper-wrapper">
        <div class="swiper-slide">Slide 1</div>
        <div class="swiper-slide">Slide 2</div>
        <div class="swiper-slide">Slide 3</div>
      </div>
      <div class="swiper-scrollbar"></div>
    </div>
    <script>
        const swiper1 = new Swiper('.mySwiper1', {
    		slidesPerView: 3,
    		spaceBetween: 50,
      		}
    		pagination: {
    			el: '.swiper-pagination',
    		},
    	});
    	const swiper2 = new Swiper('.mySwiper2', {
    	effect: 'cube',
    	grabCursor: true,
    	cubeEffect: {
    		shadow: true,
    		slideShadows: true,
    		shadowScale: 0.94,
    	},
    	pagination: {
    		el: '.swiper-pagination',
    	},
    });
    </script>

    swiper1의 경우 화면 가로 넓이가 줄어들었을 때 지나치게 길어 보여서 화면이 특정 사이즈보다 작아지면 슬라이드 개수가 3에서 1로 줄어들도록 breakpoints를 걸어줬었습니다.

    ❗문제 발생❗ '🤔화면 사이즈가 476px보다 다시 커졌을 때 원래대로 돌아가게끔 하려면 어떻게 해야 하지?'

    Breakpoint를 사용해 태블릿 기준(476px)보다 작아지면 1로 줄어들게끔 설정했더니, 다시 가로를 넓혔을 때 슬라이드 개수가 3으로 증가하지 않았습니다.

    그래서 찾아보니 breakpoint는 일반적으로 특정 조건이 충족되면 프로그램 실행 자체를 중지시키기 때문에 화면 사이즈가 한번 476px 이하로 줄어들면 함수가 더 이상 실행되지 않아 화면에 보여지는 슬라이드 개수가 영원히 1개가 되는 것입니다.

    그래서 breakpoint를 쓰면 안 된다는 걸까요?

    그렇습니다. 이 경우엔 onResize()라는 이벤트 핸들러를 사용해야 한다고 합니다.
    onResize()는 웹 크기를 조정할 때 발생하는 이벤트 처리를 위한 함수로 지금 겪고 있는 문제를 해결하기에 적합하다고 볼 수 있습니다.

    정리하자면,
    둘 다 프로그램의 변화를 감지하고 그에 따른 이벤트 처리를 해준다는 공통점이 있지만, breakpoint는 디버깅이나 프로그램이 특정 지점에 도달했을 때 실행을 중지시키는 반면, onResize()는 웹 페이지나 애플리케이션 크기를 조정할 때마다 호출되는 이벤트 핸들러의 일종으로 둘은 완전히 다른 기능을 수행합니다.

    그래서 onReszie() 함수를 이용해서 다시 웹 가로 사이즈에 맞게 슬라이드 개수를 조정해줬고, 정상적으로 잘 작동하는 것을 확인할 수 있었습니다.

    $(function () {
    	const swiper1 = new Swiper('.mySwiper1', {
    		slidesPerView: 3,
    		spaceBetween: 50,
    		freeMode: true,
    		autoplay: {
    			delay: 2000,
    			loop: true,
    		},
    		pagination: {
    			el: '.swiper-pagination',
    			clickable: true,
    		},
    	});
    	$(window)
    		.on('resize', function () {
    			const width = $(window).width();
    			if (width > 476) {
    				swiper1.params.slidesPerView = 3;
    				swiper1.params.spaceBetween = 30;
    			} else {
    				swiper1.params.slidesPerView = 1;
    				swiper1.params.spaceBetween = 30;
    			}
    			swiper1.update(); //매개변수 변경 후 재초기화
    		})
    		.resize();
    });
    

    자주 묻는 질문 (jQuery)

    화살표는 구글 아이콘을 가져와서 사용했습니다.

    Html에서 <span>을 이용해 아이콘과 질문을 묶어주었는데, 이렇게 한 이유는 각각 따로 작성하면 아이콘과 질문이 한 줄에 표현되지 않기 때문입니다. 그리고 이렇게 같은 태그로 묶어줘야 질문이든 아이콘이든 둘 중 아무거나 클릭해도 답변이 나오게끔 처리할 수 있습니다.

    <div class="questionAnswer-section">
      	<div class="qnaSectionTitle">
    		<div class="titleInner">
    			<h3>자주 묻는 질문</h3>
    		</div>
    	</div>
    	<div class="text-boxes">
    		<div class="box">
    			<ul>
    				<span>
    					<span class="material-symbols-outlined"> arrow_right</span>
                    회원가입은 어디서 하나요?
    				</span>
    				<li class="answer">
                	상단 메뉴를 보시면 '회원가입'으로 쓰여진 작은 부분이 있습니다. 해당 부분을 클릭하시면 회원가입을 할 수 있는 화면으로 이동됩니다.
                	</li>
    			</ul>
    		</div>
    		<div class="line"></div>
    	</div>
    </div>

    질문 또는 화살표 아이콘을 클릭했을 때 답이 나타나거나 사라지는 기능을 넣기 위해 JQuery를 사용했습니다.

    저희 팀에서는 저 혼자만 JQuery를 사용했는데, 제가 JQuery를 사용한 이유는 바닐라 Javascript보다 가독성이 훨씬 좋다고 느꼈기 때문입니다. 확실히 코드를 작성한지 좀 지난 뒤에 다시 봐도 무슨 내용인지 금방 이해할 수 있었고, 그렇기에 오류룰 잡을 때도 훨씬 수월했었습니다.

    여기서 ().click()부분에 들어가야 할 객체의 이름은 클라스명으로 저장했습니다.
    클래스 대신 아이디를 사용하면 (아이디는 document에서 고유식별자로 인식하기에) 이름이 같더라도 해당 객체만 적용되고 나머지 객체는 적용되지 않게 되기 때문입니다. 즉, 가장 위에 있는 질문만 클릭시 기능이 작동하고, 나머지 질문들은 아무리 클릭해도 답변이 나타나지 않는 문제가 발생하게 되는 것입니다.

    리더님을 통해 현업에서 ID는 추후 수정하거나 버그를 잡을 때 사용한다는 사실을 알게 되었습니다. 초반엔 불가피한 상황을 제외하곤 클래스를 사용하는 것이 좋다는 것을 이번 프로젝트를 통해 배울 수 있었습니다.

    $(document).ready(function () {
    	$('.box span').click(function () {
    		$(this).next('li.answer').toggle('');
    	});
    });

    ❗고민❗ 열려 있는 답변 창 개수에 따라 container의 높이 또한 자동으로 줄었다가 커질 순 없을까?

    사실 이 페이지를 구현하는 데 큰 어려움은 없었습니다. jquery기능을 넣기 전까진....

    질문이나 화살표 아이콘을 클릭하면 숨겨져 있던 답변 창이 열리지면서 전체 가로 사이즈가 길어지는데, 문제는 열려 있는 답변 창이 여러 개가 되면 Container 높이가 더 이상 늘어나지 않아 글씨가 Container 밖으로 나가거나 푸터까지 넘어가는 것이었습니다.

    미디어 쿼리를 쓰는 것은 맥락상 맞지 않는 듯 하여 어떻게 하면 간단하게 이를 조정할 수 있을지 생각해보았는데 생각보다 쉽게 해결할 수 있는 부분이었습니다.

    💡해결💡 "높이의 min과 max값을 각각 주면 되는구나!"

    바로 '높이의 min과 max값을 주는 것'이었습니다. 이렇게 하면 답변이 모두 닫혀있을 때는 container의 높이가 컴팩트하게 줄어들었다가, 답변 창이 하나씩 열릴 때마다 열린 답변 창의 높이에 따라 conntainer의 높이도 증가하게 됩니다.

    .questionAnswer-section {
    	margin-top: 88px;
    	min-height: 800px;
    	max-height: 2000px;
    	display: flex;
    	flex-direction: column;
    	align-items: center;
    	font-size: 20px;
    }

    마지막으로는 마우스를 질문이나 아이콘에 가져다 댔을 때 포인터로 바뀌게 만들어줬습니다.

    질문에 마우스를 가져다 대도 마우스에 아무런 변화가 없으면 유저는 오류가 발생한 것으로 오해할 수 있기에...

    정말 사소한 기능이지만 이러한 작은 기능들이 하나 둘씩 쌓여 유저가 편하게 이용할 수 있는 웹이 만들어지는 것이겠죠?


    아쉬운 점과 앞으로의 방향

    아쉬운 점

    1. 레이아웃

    현재 헤더와 바디의 레이아웃이 일치하지 않음

    프로젝트 기획단계에서 웹 UI/UX에 대한 고민이 많이 부족했던 것 같습니다. UI/UX가 웹에서 얼마나 큰 역할을 하는지 잘 몰랐기 때문이죠.

    그래서 굉장히 아쉬운 부분들이 많습니다.
    위의 이미지에서 보여지듯이 헤더와 바디의 좌우 여백이 맞지 않아 시각적으로 안정감 있게 보이지 않습니다. 프로젝트의 각 페이지들을 기획할 때 해당 부분을 간과한 결과라고 생각합니다.

    또 메뉴바 디자인이 웹과 모바일이 동일합니다. 모바일 유저의 입장을 고려하지 않은 것이지요.
    분명 저 또한 모바일 유저임에도 불구하고 이러한 요소를 전혀 고려하지 못했다는 사실은 상당히 부끄럽습니다.

    이번 프로젝트를 하면서 사소하다고 여겨지는 작은 부분들에 모두 웹 개발자의 고민이 녹아 들어있음을 이번에 많이 느낄 수 있었던 것 같습니다.
    그리고 프론트엔드를 희망한다면 UI와 UX에 관해서도 관심을 가져야 되겠다는 생각을 했습니다.


    2. 구현하고 싶은 기능을 다 해보지 못 한 것에 대한 아쉬움

    이번에 공지사항 게시판을 만들때 작성된 공지사항 게시물이 게시판에 등록되고, 이어서 바로 해당 공지사항이 메인 홈에도 보여질 수 있는, 즉 '실제 웹에서 볼 수 있는 공지사항'을 구현해보고 싶다는 생각을 하게 됐습니다.

    하지만 2주라는 시간은 제 생각보다 많이 짧았습니다. 나중에는 시간이 없어 메인 전체를 혼자 맡아서 해야 했는데, 이때가 아마 개발 마무리 하루 전이었을 것입니다. 이 때문에 구현해보고 싶었던 부분들은 시간상 포기할 수 밖에 없었습니다.

    앞으로의 방향

    다음 프로젝트를 하게 된다면 구현해보고 싶었던 게시판을 만들어보고 싶습니다. 만약 구현하게 된다면 여러 게시물이 생성됐을 때 페이지 넘버가 자동으로 생성되어 여러 페이지로 게시물을 관리하는 페이지네이션까지 구현해보고 싶습니다.
    profile
    Done is better than perfect🏃‍♀️

    0개의 댓글