Jindorry-Album [html, css, js] | Step 4.

진도리·2022년 4월 28일
0

Jindorry-Album

목록 보기
4/5
post-thumbnail

Process

  1. 웹 지식 +
    • 모듈화
    • Ajax
    • SPA
  2. 파일 구조
  3. 파일별 역할 또는 책임
  4. 이미지 받아오기, 이미지 삭제 변경사항
  5. JS

1. 웹 지식 +

이번 Step4에서는 Step3의 JS 코드에 대하여 모듈화를 진행하고, SPA 를 위한 디렉토리 구조를 변경 및 URL 라우팅 구현

(1). 모듈화

모듈화란 독립적인 기능을 담당하는 모듈로 분리하여 프로그래밍을 개발할 때 유지보수와 코드 재사용성을 높이는 프로그래밍 기법임

모듈이란 프로그램을 구성하는 시스템을 작은 기능 단위로 나누어 독립적으로 분리한 것.
단순히 규모가 큰 것을 작게 여러 개로 나눈 것이 아닌, 하나 이상의 논리적인 기능을 수행하기 위한 명령어들의 집합이라고 할 수 있음. 소스코드 상에서 Class, function, file 등이 이러한 모듈에 해당함

Step3 까지 진행한 프로젝트를 웹에서 보여지는 블럭을 함수형 컴포넌트로 구현했고, 이 컴포넌트를 각 독립적인 JS 파일로 생성했다. Step4 에서 모듈은 이 함수형 컴포넌트가 되겠다.

(2). Ajax

Ajax(Asynchronous JavaScript And XML)란 페이지를 업데이트 할 때, HTML 문서 전체가 아니라 json과 같은 포맷으로 서버에서 필요한 데이터만을 비동기적으로 받아와서 JavaScript를 통해 동적으로 HTML요소를 생성해서 페이지를 업데이트 하는 방식

(3). SPA

SPA(Single Page Application)란 Ajax방식을 사용하여 유저는 한 페이지에 머무르며 부분적으로 업데이트하는 방식으로 개발한 웹 어플리케이션

2. 파일 구조

└ VS code 상에서 보여지는 폴더 및 파일 구조

각 모듈마다의 역할에 따라 디렉토리 구조를 변경하고 파일을 생성했다. root 디렉토리에는 node_modules, public, server, src 총 4가지가 담겨있다. Step2 에서 변경된 디렉토리 및 파일 구조는 다음과 같다.

Single Page에 해당하는 index.html의 요소들을 동적으로 변경하는 로직이 담겨있는 모듈이나 스타일을 src 디렉토리에 담았고. src 디렉토리 내부엔 components, events, styles 디렉토리를 생성하여 역할에 따라 파일을 분리했다.

3. 파일별 역할

└ 파일간 관계

  1. index.html : SPA 에서 SP에 해당되는 HTML 파일
  2. style.css : index.html에 링크된 스타일 파일로, 동적으로 변경되는 HTML 요소의 스타일을 담고있음
  3. index.js : 브라우저에서 가장 먼저 실행시키는 js 파일로, App.js를 import 하고 있음
  4. App.js : SPA를 위한 URL 라우팅을 담당하며, MainPage.js를 import 하고 있음
  5. MainPage.js : 유저에게 보여지는 최초화면을 의미하며 CalendarInput, ImgContainer 두 개의 컴포넌트를 자신의 구역(블록)내에 렌더링
  6. CalendarInput.js : 달력 컴포넌트로, 달력의 년/월 정보가 바뀌면 MainPage 컴포넌트의 ImgContainer 컴포넌트를 재 생성하는 로직을 트리거함
  7. ImgContainer.js : 달력 컴포넌트에서 설정된 년/월 값으로 해당되는 이미지의 파일명이 담겨있는 files를 서버에서 받아와서 이미지 개수 만큼 ImgCard를 렌더링하고, files의 길이가 0일시 ReportCard로 이미지가 존재하지 않다고 화면에서 표시
  8. imgEventHandler.js : ImgContainer 컴포넌트에 존재하는 이미지에 대한 이벤트 로직을 구현한 파일, 이미지 삭제 아이콘을 클릭했을 경우 화면과 서버에서 이미지를 삭제
  9. api.js : JavaScript의 병렬 처리를 위한 Async, await 메소드로 서버에 HTTP 통신요청을 하는 로직을 담고있음
  10. ImgCard.js : Step3 에서 구현한 imgCard()를 컴포넌트 형식으로 작성
  11. ReportCard.js : 프로젝트에서의 특별한 알림을 화면에 표기하기 위한 컴포넌트
  12. Server.js : express를 이용한 서버 구현. Client단의 HTTP 요청에 따른 응답 로직을 담고있음
  13. static.js : 주로변경되지 않고 하드코딩이 필요한 값들을 const 선언자로 초기화한 변수들을 담고있음. 이 변수들을 export하여 필요한 곳에 import하여 사용

4. '이미지 받아오기', '이미지 삭제' 변경사항

메소드경로요청데이터응답데이터설명
1GET/imgyy:년도, mm:월files: 파일이름 목록해당 년/월의 파일이름 목록을 받아옴
2DELETE/imgyy:년도, mm:월, filename: 파일이름status code 204해당하는 파일을 삭제
└ step3 으로부터 요청 및 응답 변경 

└ 이미지 받아오기 (위) 이미지 삭제(아래)

Step2에선 이미지파일을 받아오기 위해, 년/월에 해당되는 이미지 디렉토리의 이미지 개수를 먼저 받아오고 { 이미지파일 받아오기 -> blob형태로 변경 후 객체 url 형성 -> 이 객체 url을 img src로 설정항여 imgCard 생성 -> imgContainer에 추가 } 의 과정을 이미지의 개수만큼 반복했다.

이미지를 받아오기위해 두 번의 HTTP 통신이 필요했고, 이로 인한 JS 코드도 길어졌다.

위 방법 대신, 년/월에 해당하는 모든 파일의 이름만을 받아와서 서버의 public 폴더에 바로 접근할 수 있도록 img src를 설정했다. 이제는 '이미지 받아오기' 라기 보단 '이미지 파일 이름 받아오기' 에 가깝다.

<img class='loaded-img' src=${imgSource} alt='loaded img idx ${imgSeq}'>
<!-- imgSource = http://localhost:3000/public/image/ + yearMonth.yy + '/' + yearMonth.mm + '/' + fileName -->

유저들에게 보여줘도 상관없는 파일들이 담겨있는 public 디렉토리를 통해 Client단에서 이미지 파일에 접근하도록 하면 한번의 HTTP 통신으로 이미지를 받아올 수 있었다.

하지만 앨범이라는 특성은 유저 개인마다 이미지를 보관하는 특성이 있는 개인정보이기에 위 방식으로 이미지 파일에 접근하는 방식은 맞지 않다. 나중에는 로그인 기능을 통해 유저마다의 인증으로 각각의 이미지에 접근할 수 있도록 구현을 해보자.

이미지 삭제는 Step2 에선 받아온 file의 index값으로 파일을 삭제했지만, '이미지 받아오기'의 로직변경으로 인해 file의 index값을 더 이상 활용하지 않기에 위 그림처럼 년/월 및 filename을 key-value 형태로 보내서 파일을 삭제요청하는 방식으로 변경했다.

5. JS

모듈화로 인해 Step3 의 하나의 JS파일이 여러개의 JS파일로 분리되었다. 이 모든걸 다 보여주기엔 무리가 있으니 ImgContainer.js를 대표로 컴포넌트를 구현할 때 고려한 내용을 적어보겠다.

import { getImg } from '../api.js';
import { imageDirURL } from '../static.js';
import { onDeleteImg, onExtendImg } from '../events/imgEventHandler.js';
import ImgCard from './ImgCard.js';
import ReportCard from './ReportCard.js';

export default function ImgContainer({ target, yearMonth, imgContainerInit }) {
  this.state = {
    files: null,
  };
  // 상태 값
  // 최초 값이 없는 경우엔 undefined 보단 null값을 할당하여 메모리 절약

  this.setFiles = (files) => {
    this.state.files = files;
  };
  // this.setStateName()으로 this.state.files 값을 변경

  const div = document.createElement('div');
  div.className = 'img-container';
  // div tag로 ImgContainer 생성

  const imgSourceHandler = (fileName) =>
    imageDirURL + yearMonth.yy + '/' + yearMonth.mm + '/' + fileName;
  // 상위 컴포넌트에서 전달받은 yearMonth값과 인자로 받은 fileName으로 imgSource를 설정하여 반환

  const renderImgCard = () => {
    let i = 0;
    this.state.files.map((file) => {
      new ImgCard({
        target: div,
        imgSource: imgSourceHandler(file),
        imgInfo: {
          yearMonth: yearMonth,
          fileName: file,
        },
        imgSeq: i++,
      }).render();
    });
  };
  // 이미지의 길이가 0보다 큰 경우 길이만큼 ImgCard 컴포넌트 렌더링

  const renderNoImageMsg = () =>
    new ReportCard({
      target: div,
      message: '이미지가 존재하지 않습니다!',
    }).render();
  // 이미지의 길이가 0인 경우 ReportCard 컴포넌트로 유저에게 메세지 전달

  this.render = () => {
    target.appendChild(div);
    {
      yearMonth &&
        getImg(yearMonth).then((files) => {
          this.setFiles(files);
          this.state.files.length ? renderImgCard() : renderNoImageMsg();
          onDeleteImg(imgContainerInit);
          onExtendImg();
        });
    }
  };
  // 렌더링될 때마다 수행되는 로직
}

로직에 의해 변경될 여지가 있는 값을 해당 컴포넌트의 상태값(this.state.files)으로 설정하고, 상태값 변경과 함께 컴포넌트를 다시 그리는 로직을 this.render() 내부에 넣으며, 상태값을 변경하고 싶을 때에는 this.setFiles() 으로 변경했다.

바닐라 JS로 컴포넌트를 구현할 경우, 상태값 변경을 위해 this.setState() 내에 this.render로 컴포넌트를 렌더링을 해야한다. 즉, this.setState()로 인해 상태값 변경과 함께 리렌더링이 이루어진다. Step4 에서는 구현한 컴포넌트는 Step3 의 기능구현을 우선적으로 진행했기에 위 규칙을 지키지 못했다. 아직 미숙한 부분이 많다 ... 😥 다음 Step에서 이 부분을 고려해서 코드를 변경해보자! 👊

Coclusion

  1. 결론
  2. Next Step?
  3. 참고 사이트 및 문헌

1. 결론

디렉토리구조 변경 및 파일생성을 진행하여 모듈화를 진행했다. SPA를 위해 URL 라우팅을 App.js에 구현했지만 아직 location.pathname == '/' 외에 다른 경로로 라우팅되는 상황은 없었다. 이후 이미지 확대기능을 추가하면서 아마도 URL 라우팅을 구현할 필요가 있을 것으로 보인다. (구현이 예상되어 미리 템플릿을 만들어 놓는 것은 안좋은 습관이다!)

ReactNative로 만든 프로젝트에선 useState hook을 통해 컴포넌트의 상태관리가 편했고, 상태값이 변경되면 알아서 리렌더링이 되었던 경험을 했었다. 이것을 바닐라 JS로 구현하려하니 최초 설계와는 다르게 올바르지 않은 방식으로 컴포넌트를 만들었다. 이후 과정에서는 올바른 컴포넌트 작성법으로 만들어보자.

2. Next Step

  • 컴포넌트 재구성
  • 이미지 업로드 및 확대

3. 참고 사이트 및 문헌

프로그래머스 FE 기출 문제 해설
https://prgms.tistory.com/113

바닐라 자바스크립트 - 고승원
http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9791165921071


👨‍💻 더 중요한 다른업무가 생겨 [Jindorry album]은 잠시 쉽니다!

profile
매일 작은 보폭이라도 앞으로.

0개의 댓글