└ 폴더 구조
<div>
, <img>
, 사진 이미지 파일리소스의 코드를 지워줌
메소드 경로 요청데이터 응답데이터 설명 1 GET /num-of-files yy:년도, mm:월 fileList.length: 파일개수 이미지 파일 개수를 받아옴 2 GET /file idx: 인덱스 fileList[idx]: idx번째 파일 순서대로 파일을 하나씩 받아옴
이에 따라 필요한 과정을 순서대로 읊자면 ...
(1). 달력에서 확인하고자 하는 앨범의 년/월을 선택 -> 'onchange' 이벤트 생성
(2). 'onchange' 이벤트에 선택된 년/월의 파일을 받아오는 http 요청 코드 구현
(3). 모든 년/월에 해당하는 폴더가 존재하는 것이 아니였기에, 존재하는 경우와 존재하지 않는 경우에 대한 핸들링이 필요
(4). 년/월 폴더에 대한 확인이 끝나면, 해당 폴더에 이미지 파일이 있는지 없는 지 확인
index.html 📝
<body>
<h1>진도리의 앨범</h1>
<br />
<p id="refer">년/월 선택</p>
<br />
<input
type="month"
id="calendar"
name="calendar"
onchange="loadBtnHandler(this.id)"
/>
<br />
<script src='../js/index.js'></script>
</body>
<body>
부분의 코드길이가 확실히 줄어듬<div>
또는 <img>
들을 없애 줌index.js 📝
let baseUrl = 'http://localhost:3001/';
const getFileLen = async (yy, mm) => {
const response = await fetch(baseUrl + 'num-of-files', {
method: 'GET',
headers: {
yy: yy,
mm: mm,
},
});
if (!response.ok) {
alert("Error : Function Name 'getFileLen'");
return;
}
return await response.json();
};
const getFiles = async (idx) => {
const response = await fetch(baseUrl + 'file', {
method: 'GET',
headers: {
idx: idx,
},
});
if (!response.ok) {
alert("Error : Function Name 'getFiles'");
return;
}
return await response.blob();
};
const loadedImgExist = () => {
for (let node of document.body.childNodes){
if (node.id === 'loadedImg'){
return true
}
}
return false
}
const loadBtnHandler = async (id) => {
if(loadedImgExist()){
document.getElementById('loadedImg').remove();
//document.body.removeChild(document.getElementById('loadedImg')); 같은역할
}
let ym = document.getElementById(id).value;
if(!ym) return; // 달력 삭제 버튼 눌렀을 경우
ym = ym.split('-');
let div = document.createElement('div');
### div.id = 'loadedImg'
getFileLen(ym[0], ym[1]).then((fileLen) => {
if (fileLen > 0) {
div.style =
'margin: 0 auto;height: 130px;width: 90vw;white-space: nowrap;overflow-x: scroll;display: block;';
for (let i = 0; i < fileLen; i++) {
getFiles(i).then((result) => {
let img = document.createElement('img');
img.style =
'height: 100px;width: 200px;object-fit: scale-down;display: inline-block;';
img.src = URL.createObjectURL(result);
img.alt = `loaded image num ${i + 1}`;
div.appendChild(img);
});
}
} else {
// 해당되는 파일이 없으면 없다고 표기
div.style = 'text-align:center;'
div.innerHTML = '이미지가 존재하지 않습니다.';
}
}).then(() => {
document.body.append(div);
});
};
async loadBtnHandler()
달력의 년/월 정보가 변경되면 (onchange event) 해당 년/월 정보로 서버로 이미지 파일을 받아오는 함수(1). loadedImgExist() 로 앨범 이미지 요청으로 인해 document.body 에 동적으로 추가된
div
정보가 있는지 확인 후, 있으면 해당 정보를 documnet.body 에서 제거
(2). 달력의 년도 및 월에 해당하는 이미지 파일의 길이를 getFileLen() 로 받아옴
(3). await 할 서버로의 요청이 존재하지 않지만, 파일을 받아오는 비동기 통신이 모두 끝난 후에 프로미스를 사용해서 생성된 이미지 컨테이너를 document.body 에 추가하기 위해 비동기 함수로 작성async getFileLen()
년/월 정보로 서버로 해당 년/월 폴더에 있는 이미지 파일의 개수를 받아오는 함수(1). 파일의 개수가 하나 이상이면, getFiles() 로 이미지 파일을 하나씩 받아와서 이 파일들을 화면에 추가 해주기 위한 스타일 적용 및,
<img>
속성값을 작성
(2). 파일이 존재하지 않으면 없다고 표기async getFiles()
파일개수 만큼 하나씩 이미지 파일을 서버에서 받아오는 함수(1). 서버에서 res.sendfile('image_path'); 로 파일을 받아오면 해당 파일을 blob(binary large object)형태로 반환
(2). 반환된 blob 파일을 새로운 객체 url로 생성하여 이 url로<img>
의 src로 사용
└ 페이지 검사 시 나오는 화면 (이미지 요청 전)
└ 페이지 검사 시 나오는 화면 (이미지 요청 후)
index.css 📝
h1 {
text-align: center;
}
#emptyImg {
text-align: center;
}
#refer {
margin: 0 auto;
text-align: center;
}
#calendar {
margin : 5px;
margin: 0 auto;
display: block;
}
app.js 📝
- 설명에 필요한 코드만을 작성!
/* 폴더 유무 체크하고 없으면 폴더 생성 */
const checkFolder = (yr, mth) => {
let yearPath = imagePath + yr;
let monthPath = imagePath + yr + '/' + mth;
if (fs.existsSync(yearPath)) {
if (fs.existsSync(monthPath)) {
// '년/월 폴더 둘다 존재'
} else fs.mkdirSync(monthPath);
} else {
fs.mkdirSync(yearPath);
fs.mkdirSync(monthPath);
}
return monthPath;
};
let monthPath = '';
let fileList = [];
/* 년/월 폴더의 파일 개수를 응답 */
app.get('/num-of-files', function (req, res) {
monthPath = checkFolder(req.headers.yy, req.headers.mm);
fs.readdir(monthPath, (err, files) => {
if (err) {
console.log(err);
res.status(500).send('server error');
}
fileList = files.map((item) => path.join(monthPath, item));
res.json(fileList.length);
});
});
/* 순서대로 파일을 보내기 위해 idx 값을 사용하여 fileList[idx] 로 이미지 파일 전송 */
app.get('/file', function (req, res) {
res.sendFile(fileList[req.headers.idx])
});
의외로 복병이었던 server 코드...
결국 이미지 파일을 하나씩 받아와서 다 받아오면 그 때 dom.body 에 해당 파일들을 담고 있는 이미지 컨테이너를 추가하는 코드를 작성
(1). 먼저 폴더유무 체크하고 없으면 폴더 생성
(2). fs 모듈로 해당 폴더에 접근해서 fileList에 이미지 파일의 절대 경로를 하나씩 넣어줌
(3). 이미지 파일 개수를 클라이언트로 전송
(4). req로 받아온 idx값을 통해 순서대로 이미지 파일을 전송
예를들어 2021년 7월의 7개의 이미지 파일을 받아오기 위해선, 2021년 7월에 해당하는 폴더루트에 이미지 개수 7개를 응답할 때 1번, 순서대로 하나씩 이미지 파일을 전송하기 위해 7번. 총 8번의 fetch() 함수가 실행됨.
React.js 를 사용하여 웹페이지를 생성했을 때는 React 라이프 사이클, React-hook을 이용해서
화면에 렌더링할 것들을 업데이트했던 것에 비하면 불편한 점들이 있었음
html, js, css 만을 사용해서 스크린을 새 스크린으로 업데이트 한다기 보단(rerender), 이벤트를 생성하여 이에 대한 핸들링을 통해 dom-tree에 동적으로 추가 및 삭제하는 것임
단순 업데이트가 아닌 추가에 대한 삭제하는 코드도 곁들여야 하기에 코드 길이와 컴파일 시간이 길어질 수 있다는 생각이 들었음
blob (binary large object)
- 바이너리 형태의 큰 객체
- 주로 이미지, 비디오, 사운드 등과 같은 멀티미디어 객체들을 가리킴
URL.createObjectURL( file )
- 특정 파일 객체나 로컬 파일 또는 데이터의 참조를 가리키는 새로운 객체 URL을 생성
- 객체를 생성한 문서 내에서만 유효