Diary/Retrospective

[항해99 / 온보딩주차] 🔎 풀스택 미니 프로젝트 회고

Olivia Kim 2023. 4. 6. 23:30
반응형

 

프로젝트 소개

프로젝트 명

DevView

 

 

기획 의도

개발자 취준생들이 서로 추천하는 개발 관련 유튜브 링크를 아카이브하고 모아볼 수 있는 웹을 만들고자 했다.

 

 

 


프로젝트 설명

프로젝트 기간

2023-03-27 15:00 ~ 2023-03-30 18:00

 

 

사용 기술

온보딩주차 필수 강의였던 웹개발종합반에서 실습했던 것들 위주로 사용하였다.

Design Figma
Front-end HTML, CSS, JavaScript
Back-end Flask, MongoDB

 

 

구현하고자 한 기능 / 구현하지 못한 기능

기능 스코프 개발 여부
메인화면 1차
네비게이션바 1차
글 작성 1차
상세페이지 리스트 조회 1차
상세페이지 검색기능 2차
상세페이지 댓글 1차
상세페이지 별점 조회 2차
상세페이지 페이지네이션 2차

 

 

프로젝트에서 나의 역할

  • 반응형 메인페이지, 네비게이션바
  • 상세페이지 html, css 레이아웃 구현
  • 상세페이지 리스트 조회

 

 

 


KPT로 프로젝트 돌아보기

 

📢 KPT는 구현한 화면 / 기능에 대해서만 작성했습니다.

 

메인페이지

 

메인페이지는 네비게이션바와 상세페이지 카테고리를 시각화하여 간단하게 보여주었다. 네비게이션바는 hover와 action 을 주었고, 메인페이지 로고 아래의 카테고리 사진들은 해당 카테고리로 이동 가능하도록 각각 a태그로 감쌌다. 슬라이드는 swiper.js를 이용해 무한으로 흘러가는 이벤트를 구현했다.

 

 

const swiper = new Swiper(".mySwiper", {
  // 슬라이드 자동재생 비활성화되지 않도록 함
  autoplayDisableOnInteraction: false,
  // 슬라이드 반복 시 마지막 슬라이드에서 첫 번째 슬라이드로 넘어가도록 설정
  loopAdditionalSlides: 1,
  // 연속 루프모드 설정 여부
  loop: true,
  // 슬라이드 전환 ms 속도
  speed: 2000,
  // 한 페이지에 몇 개의 슬라이드를 보여줄 것인지 설정
  slidesPerView: 3,
  // 슬라이드 간 공백
  spaceBetween: 30,
  // 자동재생 옵션
  autoplay: {
    deplay: 0,
    // 다음 슬라이드로 전환할 때 몇 ms를 지연할 것인지 설정
    delay: 2000,
  },
  // 페이지네이션 옵션
  pagination: {
    // 페이지네이션 html 요소
    el: ".swiper-pagination",
    // 페이지네이션 클릭 가능 여부
    clickable: true,
  },
});

 

 

 

 

또한 메인페이지와 네비게이션바는 1200px을 기준으로 반응형 화면으로 제작했다. 1200px 이하일 때는 햄버거 아이콘으로 메뉴가 토글될 수 있도록 했고, hover 이벤트도 동일하게 적용했다.

 

 

Keep

  • 공식문서 참고하기

맨 처음엔 메인페이지 슬라이드를 무한루프로 구현할 계획은 없었다. 하지만 마지막 슬라이드에서 이벤트가 뚝 끊겨버리는게 시각적으로 아쉬워 무한루프를 생각하게 됐고, swiper.js 공식문서와 블로그를 뒤져가며 구현할 수 있는 코드를 찾아냈다. 흐름의 끊김없이 더 자연스러운 화면이 된 것 같아 만족스럽고, 앞으로도 공식문서를 찬찬히 읽어보며 내가 원하는 내용을 가져올 수 있는지 찾아보는 습관을 이어나가고 싶다.

 

 

  • (시간이 허용되는 범위라면) 안되면 될 때까지 해보기

네비바도 초반 기획에서는 반응형 계획이 없었지만 구현이 생각보다 빨리 끝나 반응형을 추가하게 되었다. CSS를 이용해 반응형을 구현하는 흐름이 헷갈려 열심히 코드를 고치고 또 고치며 결국 구현해냈다. 역시 중요한건 꺾이지 않는 마음..!

 

 

Problem

  • 네비바와 다른 페이지 간의 코드 호환 문제

메인페이지에서는 네비바 적용이 잘 되지만 상세페이지에서는 화면을 작게 만들고 스크롤을 할 경우 상세페이지 요소들과 겹쳐보이는 현상이 발생한다. 네비바는 거의 모든 페이지에서 적용하기 때문에 다양한 화면을 고려해서 코드를 맞춰놔야한다는 것을 생각하지 못해 아쉽다.

 

 

  • jinja2를 사용하는게 최선이었는지에 대한 고민

네비바를 모든 페이지에서 불러오기 위한 방법을 계속 고민했다. 상세페이지에서 네비바를 불러올 때 import하듯 불러오고 싶어 해당 방법처럼 html안에 html을 넣는 방법이 없는지 계속 찾아보았다.

 

 

// html
<nav id="navbar"></nav>

// javascript
document.addEventListener("DOMContentLoaded", function(){
  const temp_nav = `
    <div class="navbar__logo">
      DevView
    </div>
    <ul class="navbar__menu">
      <li><a href="#">HTML&CSS</a></li>
      <li><a href="#">JavaScript</a></li>
      <li><a href="#">React</a></li>
      <li><a href="#">TypeScript</a></li>
    </ul>
    <div class="navbar__team">
      항해99 14기 17조
    </div>
  `;

  document.querySelector('nav').innerHTML = temp_nav;
 });

 

순수 html안에 html을 넣으려면 영역을 만들고 innerHTML로 만드는게 지금의 최선이었다. 하지만 코드가 길어지면? 상세페이지가 더 많아지면? 그때마다 이벤트를 거는건 비효율적이라는 생각이 들었다. 그래서 추가적인 방법을 더 찾아보았다.

 

 

{% include 'navbar.html' %}

 

이전에 고민했던대로 import하는 형식으로 네비바를 붙일 수 있는 방법이 있었다. 바로 jinja2. jinja2는 파이썬에서 동작하는 템플릿 엔진으로, 특정 데이터와 템플릿을 연결해서 동적인 움직임을 지원한다. jinja2가 해당 HTML코드를 템플릿으로 만들면, 템플릿 내의 존재하는 파이썬 코드를 실행시켜 템플릿을 채운 후 최종 HTML파일을 생성한다.

 

pip install을 이용해 jinja2를 설치하고 상세페이지에서 위의 코드 한 줄을 추가하니 문제없이 네비바를 불러올 수 있었다. 또한 include하는 방식이기 때문에 다른 페이지에서 공통으로 사용하는 CDN들을 네비바 한 군데에만 몰아넣어 중복없이 코드를 작성할 수 있었다.

 

다만 위에 기술한대로 jinja2는 '템플릿 내의 존재하는 파이썬 코드'를 같이 실행시켜주는 역할을 하는데, 우리가 작성한 코드에서 다른 이벤트 처리는 모두 바닐라JS로 처리했기 때문에 파이썬을 이용해 if문을 쓰거나, 변수를 불러오거나 하는 일은 없었다. 그래서 상세페이지가 아주 많았다면 효율적이었겠다고 할 수 있겠지만, 지금은 메인페이지와 상세페이지 하나 뿐인데 저 한 줄을 추가하려고 jinja2를 설치하는게 옳은가? 라는 고민이 계속 들었다. 해당 시간 내에 찾아본 바로는 jinja2외의 별다른 방법을 찾지 못했지만 include 한 줄을 위해 패키지를 설치했다는게 계속 찜찜했다. 패키지 설치 개수에 따라서 웹페이지 속도가 느려지는지도 궁금하고. 추후에 성능 최적화에 대해 공부해보고 싶기 때문에 이 부분은 나중에 최적화를 공부하면서 같이 체크해보고 싶다.

 

 

Try

  • 코딩 컨벤션을 맞춘다.

상세페이지에서 네비바가 겹쳐보이는 이유는 클래스명이 중복되는 경우도 있었고 CSS 요소를 사용할 때 display나 position을 이어지지 않게 사용하기 때문이었다. 이번 프로젝트는 항해에서 처음 진행하는 팀 프로젝트였고 기간이 짧았기 때문에 코딩 컨벤션까지 상세히 맞추기 어려웠지만, 다음 팀 프로젝트에서는 코딩 컨벤션을 맞춰서 누가 작업해도 일관성있게 보이는 코드를 작성해보고 싶다.

 

 

 

상세페이지 - 화면

 

네비바나 메인페이지를 통해 상세페이지를 들어오면 위와 같은 화면을 볼 수 있다. 맨 위는 해당 카테고리의 이름을 보여주고 그 아래 검색창, 글 작성 버튼, 글 리스트를 만들어두었다. 검색창과 버튼, 썸네일은 모두 각기 다른 hover를 걸었다. 상세페이지에서 팀원들과 함께 구현한 부분은 리스트 가져오기, 썸네일 가져오기, 썸네일 예외처리이다.

 

 

Keep

  • 코드 리팩토링하기

프로젝트 막바지에는 모두가 코드를 바로바로 수정해서 넘겨줘야 했기에 github에 debugger를 작성해둔 부분까지 push하거나, HTML에 인라인으로 CSS를 작성하거나 하는 부분이 있었다. 프로젝트가 끝난 후 코드를 쭉 되돌아보며 불필요한 코드와 사용하지 않는 변수, 의도와 다르게 나오던 CSS를 다시 수정했다. 리팩토링이라는 단어를 붙이기엔 조금 거창하지만 어쨌든 내 이름도 함께 걸려있는 작업물이니 프로젝트가 끝났어도 깔끔하게 관리하고 싶었기 때문이다. 결과적으로 의도하던대로 CSS가 나오도록 수정하기도 했고 불필요한 코드를 남겨두지 않았다는 마음의 안도감이 들어서 좋았다.

 

 

Problem

  • 유튜브 링크가 아닐 경우, 또는 유튜브 링크 형식이 다를 경우의 처리

DB에 저장된 유튜브 링크를 이용해 화면에 가져오는 방법은 다음과 같다.

 

 

<img src="https://i1.ytimg.com/vi/유튜브주소중해당링크를식별할수있는부분/sddefault.jpg" alt="">

 

위의 식별할 수 있는 부분을 link라는 변수로 처리해 각 링크마다 자신의 썸네일을 가져올 수 있도록 하였다.

 

 

 

유튜브 링크는 같은 영상이라도 브라우저 주소창에서 가져오는지, 영상 상세페이지 내 공유버튼을 클릭해 주소를 복사하는지에 따라 유튜브 주소가 다르게 생성된다. 우리가 썸네일을 가져오기 위해서 필요한 부분은 주소창의 v= 다음 부분, 공유버튼을 클릭해 보이는 주소 중에서는 .be/ 다음 부분이다. 따라서 처음엔 앞에서부터 필요한 부분 자르는 형식으로 구현했으나 링크 길이가 각각 달라 팀원분이 수정해주신로 뒤에서부터 slice해 해당 값을 가져오는 식으로 구현했다.

 

 

// forEach로 리스트 데이터 화면에 뿌려주는 부분
rows.forEach((element) => {
    const videolink = element['videolink'];
    const thumbnail = videolink.slice(32);
    let link = `https://i1.ytimg.com/vi/${thumbnail}/sddefault.jpg`;

    const temp_html = `
      <div class="item">
        <div class="itemImage">
          <a href="${videolink}" target="_blank">
            <img src="${link}" alt="">
          </a>
        </div>
        <div class="itemDesc">
          <h1>${element['videoname']}</h1>
          <p>${element['videodesc']}</p>
        </div>
      </div>
    `;
    document.querySelector('.list').insertAdjacentHTML('beforeend', temp_html);
  })
}

 

 

 

그런데! 유튜브는! 재생목록에서 해당 영상을 클릭해 들어올 때는 주소창에서 또 다른 값을 보여준다. 프로젝트 구현 당시에는 시간이 없어 미처 구현하지 못했지만 지금 생각해보니 이 모든 경우를 포함하려면 if-else로 주소가 youtube.com/watch?v='고유값' 인지, youtube.be/'고유값'인지를 판별해 각각 slice 범위를 처리하면 될 것 같다.

 

 

또한 사용자가 입력한 주소가 유튜브가 아닐 경우, 우리는 썸네일을 가져올 때 유튜브 썸네일만 가져올 수 있는 방식을 취하고 있으므로 아무런 썸네일을 가져올 수 없게 된다. 이러한 경우를 방지하기 위해 해당 썸네일 고유값을 가져올 수 없는 경우에는 우리가 지정해준 팀원들 캐릭터 사진을 썸네일로 보여주도록 처리하고 있다.

 

    if (thumbnail.length === 11) {
      link = `https://i1.ytimg.com/vi/${thumbnail}/sddefault.jpg`;
    } else {
      link = '../static/images/default.png';
    }

 

유튜브 링크 고유값의 길이가 11이었으므로 일치한다면 유튜브 썸네일을 가져오는 주소를 링크로, 아니면 프로젝트 내에 옮겨둔 default 사진이 썸네일로 보여지도록 했다. 하지만 사용자들이 유튜브 링크만 가져올 것이라는 확신이 없고, 해당 상세페이지 내에서도 유튜브 링크만 가능하다는 고지를 따로 해두지 않았다. 따라서 화면 내에 가능하다고 고지를 해두거나 글 작성 시 링크 유효성 체크를 하여 유튜브 링크가 아니면 글이 작성되지 않도록 (DB에 데이터가 등록되지 않도록) 하면 더 보기 좋은 화면을 만들 수 있을 것 같다. 

 

 

  • 제목이 해당 div의 길이를 넘어갈 경우 옆으로 넘어가거나, 아래로 빠져나오는 문제

지금의 코드는 사용자가 긴 제목을 입력했을 때, 제목이 영역을 벗어나 옆으로 넘어가게 된다. 리팩토링을 할 때 그 부분을 수정하기 위해 width를 제한하고, ellipsis나 nowrap를 설정하는 등 다양한 시도를 해보았으나 원하는대로 화면에 보여지지 않았다.

 

 

Try

  • 다양한 예외처리에 대해 고민한다.

사용자가 우리가 원하는대로만 입력할 수 있도록 웬만한 경우의 수에 다 제한을 걸거나, 이렇게만 가능하다고 친절하게 안내해주지 않는 이상 의도와 다른 데이터가 들어올 수 있을것이다. 유튜브 링크의 경우 우리가 어떤 링크만을 가능하게 할 것인지 크게 제한을 두지 않았고, 예외처리도 상세히 해두지 않았기 때문에 링크 테스트를 할 때 썸네일이 어떻게 보여지는가에 대한 문제가 있었다. 그렇다고 모든 입력의 경우의 수에 제한을 두면 사용자의 입장에서는 불편할 것 같기도 하고. 사용자의 다양한 입력값을 적당히 포함할 수 있으면서 불가능한 예외에 대해서는 코드로 입력을 차단하는 포용력있으면서도 단호한 코드를 구현해보고 싶다. 이게 무슨 뜨거운 아이스아메리카노같은 말인지 모르겠지만..

 

 

  • CSS의 개념을 정리한다.

글자 길이가 길어질 때 해당 범위를 넘어가는 문제에 대해서 글 작성 시 input에 제한을 걸 수도 있겠지만 그렇게 되면 해당 요소 폭이 좁아 사용자가 입력할 수 있는 글자 수가 너무 적기 때문에 max-length로 제한하기보다 CSS로 유연하게 처리하고 싶었다. 하지만 아직 display, position에 대한 이해가 부족한지 이것저것 해봐도 내가 의도한대로 해당 줄의 길이보다 글자수가 더 크면 한 줄 내에서 ...처리로 보이도록 하는게 나오지 않았다. 이에 대해서는 관련 CSS 개념을 좀 더 확실히 정리하고 어느 부분을 내가 놓치고 있는건지 점검해봐야겠다.

 

 

 

상세페이지 -  글 작성

 

공유하고 싶은 링크가 있다면 상세페이지의 추천영상공유 버튼을 클릭해 글 작성 모달을 열 수 있다. URL input에 유효성검사를 걸어두어 URL을 입력하지 않았거나, URL 형식이 아닐경우 alert를 띄우고 글을 작성할 수 없다. 작성된 글은 리스트의 가장 아래에 보여지며 썸네일 클릭 시 해당 링크로 이동해 영상을 시청할 수 있다.

 

 

 

상세페이지 - 검색

 

검색한 키워드를 포함하고 있는 제목을 검색할 수 있다. 이때 일치하는 키워드를 가진 영상이 없다면 alert를 띄워주고 화면에 아무것도 보여주지 않는다. 별다른 키워드 없이 빈 칸으로 검색버튼을 클릭할 경우 전체 리스트를 보여준다.

 

 

 


트러블슈팅

유효한 url, id, pwd를 입력했음에도 팀원 마다 pymongo 에러가 발생하는 경우도 있고, 아닌 경우도 있어 한참을 헤맸는데 인터넷 환경에 따른 보안 문제였다. 해당 오류는 다음과 같이 해결했다.

 

 

[Python] pymongo.errors.ServerSelectionTimeoutError

오류 발생 경로 app.py에 mongoDB의 경로, 아이디, 패스워드까지 제대로 입력했음에도 불구하고 다음과 같은 에러가 발생했다. 오류 내역 pymongo.errors.ServerSelectionTimeoutError: ac-offmh0k-shard-00-02.grjwzgf.mong

oliviakim.tistory.com

 

 

  • 고유 ID를 생성할 때 Date.now()와 UUID 중 어느 것이 더 좋은지?

트러블슈팅은 아니고 고민했던 부분에 대한 해답. 원래는 처음 기획에 댓글도 포함되어있어서 글과 댓글의 고유 ID를 DB에 넣어야 했다. 찾아보니 Date.now와 UUID를 많이 사용하는데, Date.now는 해당 순간의 ms를 보여주는 것이기 때문에 정말 낮은 우연의 일치로 같은 시간에 글을 작성했을 경우 Date.now가 유니크하지 않고 중복되어서 들어가는게 아닌가 하는 생각이 들었다. 이 부분에 대해 기술매니저님께 여쭤보니 질문한대로 사용자가 많고 들어오는 데이터가 많을 경우 Date.now는 중복의 위험성이 있어 UUID가 더 낫다고 말씀해주셨다.

 

다만, 우리는 mongoDB를 사용하고 있으므로 mongoDB에서 자체적으로 만들어주는 objectID가 제일 유니크하다고 말씀해주셨다! 그게 mongoDB의 장점 중 하나라고 한다. 그렇다면 pip install로 UUID를 굳이 설치할 필요도 없고 만들어진 값만 불러오면 되니 좋다는 생각이 들었다. mongoDB는 이런 장점이 있구나 또 하나 배운 부분이었다. 

 

 

  • 맥에서 git 사용 문제

맥을 사용중인 팀원 분이 계속 git 연결이 제대로 되지 않는 문제가 발생했다. 알고보니 맥은 보안을 위해 별도의 SSH 설정을 해줘야 하며, github에서 clone을 해올 때도 SSH 주소로, push를 할 때도 SSH 주소로 해야 된다고 한다. 나중에는 맥을 사용할 생각이 있기 때문에 해당 부분을 잊지 않도록 같이 작성해둔다!

 

 

 


그 외 문서들

팀 피그마

 

이번주 WIL에서도 언급했었지만 UX/UI 디자인을 공부하셨던 팀원 분께서 피그마를 이용해 와이어프레임을 정말 꼼꼼하게 짜주셨다. 피그마는 처음 사용해봤는데 미리 정해둔 마진, 픽셀값등을 볼 수 있어 통일성있는 디자인을 만들기 좋았고 HTML, CSS를 구현할 때도 동일하게만 만들면 되니 디자인 고민없이 만들 수 있어 정말정말 좋았다. 디자이너분과의 협업을 이렇게 하는거구나!를 조금 맛볼 수 있었다.

 

 

 

팀 노션

 

원래도 정리하고 문서화하는걸 좋아하긴 하지만 첫 팀 작업이라 함께 맞추고 싶은 부분들을 팀 노션에 모두가 편하게 볼 수 있게 정리해두었다. 노션이 마크다운 기반이라 깔끔하게 정리하는데 워낙 편하기도 하고 손에 익어서 오히려 다른 문서로 작성하는 것보다 빨리 작성할 수 있다. 이번에 새로 만들어본 것은 데이터베이스를 이용한 칸반보드였는데 현재 어느 부분을 작업 중인지, 어느 부분을 구현하지 못했는지 시각적으로 확인할 수 있어 좋았다. 이게 그 유명한 지라 티켓을 끊어온다는거랑 비슷한건가?

 

 

 


 

✅ 다른 팀원 분들은 온보딩 1, 2주차를 같이 보내셨고 나는 온보딩 3주차에 새로 합류하게 됐는데 팀원분들이 스스럼없이 대해주셔서 팀에 잘 녹아들 수 있었다. 대화도 잘 되고 협업도 잘 되고 분위기도 좋아서 앞으로도 이렇게 팀 프로젝트가 진행됐으면 좋겠다는 생각이 들었다.

 

[DevView Github]

 

GitHub - devView-hanghae/devView: 항해99 풀스택 미니 프로젝트 17조

항해99 풀스택 미니 프로젝트 17조. Contribute to devView-hanghae/devView development by creating an account on GitHub.

github.com

 

[DevView Notion]

 

[미니풀스택] Dev View

개발과 관련된 유용한 링크들을 카테고리별로 공유해보세요!

ethereal-ethernet-87a.notion.site

 

 


[참고 자료]

https://github.com/JaeYeopHan/tip-archive/issues/8

 

회고 (Retrospective)에 대한 정리 및 설계 · Issue #8 · JaeYeopHan/tip-archive

What? 회고란 무엇인가 사전적 정의를 먼저 살펴보자면, "돌아다봄", "지나간 일을 돌이켜 생각함" 등을 의미한다. '프로젝트 회고'라는 부분으로 구체화시키는 경우, 프로젝트의 큰 생명 주기에서

github.com

https://brunch.co.kr/@jinha0802/35

 

KPT 회고란 무엇인가?

스타트업에서 KPT 회고는 언제 필요하며, 왜 해야 하는가? | 1. KPT 회고란? KPT회고는 다양한 회고 방법론 중 하나이다. Keep, Problem, Try의 약자로 회고 내용을 세 가지 관점으로 분류하여 회고를 진행

brunch.co.kr

https://unpasoadelante.tistory.com/159

 

Flask - jinja2 template

Flask 입문 수업을 듣고 중요한 내용을 정리했습니다. 개인 공부 후 자료를 남기기 위한 목적이므로 내용 상에 오류가 있을 수 있습니다. jinja2 template(Python) jinja2 template를 사용하여, 파이썬(flask)

unpasoadelante.tistory.com

https://scribblinganything.tistory.com/387

 

[Python] Flask란? (WSGI, Werkzeug, Jinja2, Web framework)

목차 플라스크란? (What is Flask?) Flask란 파이썬에서 사용 가능한 웹 어플리케이션 프레임워크(Web Application Framework)입니다. Pocco라는 파이선개발자 그룹의 Armin Ronacher에 의해 개발되었습니다. Flask는

scribblinganything.tistory.com

 

 

 

반응형