web에 대한 이해 없이 spring으로 rest api만 만들왔다.
게시판 개발 과정에서 "여러개의 client에서 데이터를 받아서 하나의 시퀀스로 처리하는 서버 로직을 만들어봐라. 즉, input이 여러개인 아키테쳐를 설계해봐라" 라는 요구사항이 들어왔을 때 기술적으로 어떻게 그것이 가능한지 몰랐다.
직접 만들어보면서 브라우저의 동작 매커니즘을 이해하기 위해 본 프로젝트에 참여했다.
3명의 42서울 카뎃이 개포 클러스터에 모여서 매일 2시간동안 학습 및 코딩을 했다.
90분동안 자율 개발하고 30분 동안 서로의 코드를 보면서 리뷰했다.
github의 이슈 관리 툴을 사용하여 서로의 진행상황과 좋은 레퍼런스를 공유했다.
2022.05.09 ~ 2022.06.02
mission1에서는 영상 콘텐츠의 메인 화면을 구현한다.
레이아웃,다양한 형태의 팝업(layer popup, alert, popup etc),이벤트 핸들링, 브라우저에서 히스토리를 남기는 매커니즘, DOM tree 조작, browser Storage 등에 대해 배워갈 수 있다.
유튜브 화면 비스무리하게 만들어지는 여정 시이~~작
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;">
<meta charset="UTF-8" />
<style>
.container {
display: flex;
flex-direction: row;
justify-content: space-between;
justify-content: center;
}
img {
}
figure {
width: 400px;
height: 400px;
background-color: yellow;
margin-top: 100px;
margin-bottom: 100px;
margin-left: 100px;
margin-right: 100px;
}
</style>
</head>
<body>
<header>
</header>
<main>
<section class="container">
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers" width="350" height="263">
<figcaption>[ 그림 1. 위의 그림은 이쁜 꽃이네요! ]</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers" width="350" height="263">
<figcaption>[ 그림 1. 위의 그림은 이쁜 꽃이네요! ]</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers" width="350" height="263">
<figcaption>[ 그림 1. 위의 그림은 이쁜 꽃이네요! ]</figcaption>
</figure>
</section>
<section class="container">
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers" width="350" height="263">
<figcaption>[ 그림 1. 위의 그림은 이쁜 꽃이네요! ]</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers" width="350" height="263">
<figcaption>[ 그림 1. 위의 그림은 이쁜 꽃이네요! ]</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers" width="350" height="263">
<figcaption>[ 그림 1. 위의 그림은 이쁜 꽃이네요! ]</figcaption>
</figure>
</section>
<section class="container">
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers" width="350" height="263">
<figcaption>[ 그림 1. 위의 그림은 이쁜 꽃이네요! ]</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers" width="350" height="263">
<figcaption>[ 그림 1. 위의 그림은 이쁜 꽃이네요! ]</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers" width="350" height="263">
<figcaption>[ 그림 1. 위의 그림은 이쁜 꽃이네요! ]</figcaption>
</figure>
</section>
</main>
<footer>
</footer>
</body>
</html>
무식하게 다 때려넣어버리기 신공
코드 보기자문:
두줄 출력.. 그거 어케하는거죠?
자답: line-height 설정하고 그 값 * [출력하고 싶은 줄 수] 에 해당하는 값을 height에 넣으면 되는구나!!
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;">
<meta charset="UTF-8" />
<style>
.container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
}
figure {
width: 20vw;
height: 30vh;
flex-basis: auto;
margin-top: 1rem;
margin-bottom: 1rem;
margin-left: 1rem;
margin-right: 1rem;
}
img {
width: 100%;
height: 80%;
}
figcaption {
width: 100%;
height: 20%;
background-color: yellow;
font-size: 1rem;
}
div.video-title {
font-size: 100%;
width: 80%;
height: 30%;
text-overflow: ellipsis;
overflow : hidden;
white-space : nowrap;
margin-bottom: 0rem;
margin-top: 0rem;
}
div.video-metadata {
font-size: 80%;
width: 90%;
display: inline-block;
overflow : hidden;
text-overflow: ellipsis;
white-space: normal;
line-height: 1.2;
height:2.4em;
text-align: left;
word-wrap: break-word;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
</style>
</head>
<body>
<header>
</header>
<main>
<section class="container">
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers">
<figcaption>
<div class="video-title">This is title This is titleThis is titleThis is titleThis is title</div>
<div class="video-metadata">
This is first line .This is first line .This is first line .This is first line .This is first line .This is first line .This is first line .This is first line .This is first line .This is first line .
</div>
</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers">
<figcaption>
<div class="video-title">This is title</div>
<div class="video-metadata">
This is first line .This is first line .This is first line .This is first line .This is first line .
This is first line .This is first line .This is first line .This is first line .This is first line .
</div>
</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers">
<figcaption>
<div class="video-title">This is title</div>
<div class="video-metadata">
This is first line .This is first line .This is first line .This is first line .This is first line .
This is first line .This is first line .This is first line .This is first line .This is first line .
</div>
</figcaption>
</figure>
</section>
<section class="container">
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers">
<figcaption>
<div class="video-title">This is title</div>
<div class="video-metadata">
This is first line .This is first line .This is first line .This is first line .This is first line .
This is first line .This is first line .This is first line .This is first line .This is first line .
</div>
</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers">
<figcaption>
<div class="video-title">This is title</div>
<div class="video-metadata">
This is first line .This is first line .This is first line .This is first line .This is first line .
This is first line .This is first line .This is first line .This is first line .This is first line .
</div>
</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers">
<figcaption>
<div class="video-title">This is title</div>
<div class="video-metadata">
This is first line .This is first line .This is first line .This is first line .This is first line .
This is first line .This is first line .This is first line .This is first line .This is first line .
</div>
</figcaption>
</figure>
</section>
<section class="container">
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers">
<figcaption>
<div class="video-title">This is title</div>
<div class="video-metadata">
This is first line .This is first line .This is first line .This is first line .This is first line .
This is first line .This is first line .This is first line .This is first line .This is first line .
</div>
</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers">
<figcaption>
<div class="video-title">This is title</div>
<div class="video-metadata">
This is first line .This is first line .This is first line .This is first line .This is first line .
This is first line .This is first line .This is first line .This is first line .This is first line .
</div>
</figcaption>
</figure>
<figure>
<img src="../images/abc.jpeg" title="설명글" alt="flowers">
<figcaption>
<div class="video-title">This is title</div>
<div class="video-metadata">
This is first line .This is first line .This is first line .This is first line .This is first line .
This is first line .This is first line .This is first line .This is first line .This is first line .
</div>
</figcaption>
</figure>
</section>
</main>
<footer>
</footer>
</body>
</html>
두줄 출력하는데 참고한 레퍼런스 => http://www.kunwi.co.kr/style/584
alert 창
코드 보기let video_title = document.querySelector('.video-title');
video_title.onclick = () => alert(video_title.textContent);
팝업 창
코드 보기function video_popup(e){
let win= window.open("", "_blank", "toolbar=yes,scrollbars=yes,resizable=yes,top=500,left=500,width=400,height=400");
win.document.write(`<h1 id="video-title">${e.target.textContent}</h1>\`);
}
let video_titles = document.querySelectorAll('.video-title');
for (let i = 0; i < video_titles.length; i++) {
video_titles[i].addEventListener('click', video_layer_popup);
}
레이어 팝업
코드 보기
#popup_bg {
display: none;
position: absolute;
top:0;
left:0;
width:100%;
height:100%;
background:rgba(0,0,0,0.5);
}
#popup_bg > #popup {
position:absolute;
padding:15px;
box-sizing:border-box;
border-radius:15px;
top:50%;
left:50%;
transform:translate(-50%, -50%);
width:600px;
height:400px;
background:#fff;
box-shadow: 7px 7px 5px rgba(0,0,0,0.2);
}
#popup_bg > #popup > h2 {
margin-bottom:25px;
}
.popup-close {
float: right;
}
function video_layer_popup(e) {
let title = e.target.textContent;
let layer_popup = document.querySelector('#popup_bg');
let popup_title = document.querySelector('#popup-title');
popup_title.innerHTML = title;
layer_popup.style.display = 'block';
}
let video_titles = document.querySelectorAll('.video-title');
for (let i = 0; i < video_titles.length; i++) {
video_titles[i].addEventListener('click', video_layer_popup);
}
let layer_popup_close = document.querySelector('.popup-close');
layer_popup_close.addEventListener('click', () => {
let layer_popup = document.querySelector('#popup_bg');
layer_popup.style.display = 'none';
});
<div id="popup_bg">
<div id="popup">
<h2 id="popup-title">This is mtak layer
</h2>
<button type="button" class="popup-close">X</button>
</div>
</div>
//방법1개 - html 이용
<a href="http://127.0.0.1:5500/sub4/detail.html">
<img src="http://127.0.0.1:5500/images/first.webp" title="설명글" alt="flowers">
</a>
//방법4개 - main.js
function goDetail(e) {
let fig = e.target.closest('figure');
localStorage.setItem('main-video-stream', e.target.src);
localStorage.setItem('main-video-title', fig.querySelector('.video-title').textContent);
localStorage.setItem('main-video-content', fig.querySelector('.video-metadata').textContent);
// localStorage.setItem('main-video-title', e.target.closet('figure'))
// location.assign("http://127.0.0.1:5500/sub4/detail.html");
location.href = "http://127.0.0.1:5500/sub4/detail.html";
// location.replace("http://127.0.0.1:5500/sub4/detail.html");
}
let video = document.querySelector('img');
for (let i = 0; i < video.length; i++) {
video[i].addEventListener('click', goDetail());
}
//detail.js
let main_video_stream = document.querySelector(".main-video-stream");
let img = document.createElement("img");
img.src = localStorage.getItem("main-video-stream");
img.style.width = "100%";
img.style.height = "100%";
main_video_stream.appendChild(img);
let main_video_title = document.querySelector(".main-video-title");
let main_video_content = document.querySelector(".main-video-content");
main_video_title.textContent = localStorage.getItem("main-video-title");
main_video_content.textContent = localStorage.getItem("main-video-content");
//detail.html
<body>
<div class="main-video">
<div class="main-video-stream"></div>
<div class="main-video-description">
<div class="main-video-title"></div>
<div class="main-video-content"></div>
</div>
</div>
</body>
.main-video-content {
font-size: 80%;
width: 90%;
display: inline-block;
text-align: left;
word-wrap: break-word;
white-space: normal;
}
.showstep1 {
overflow : hidden;
text-overflow: ellipsis;
line-height: 1.2;
max-height: 4.8em;
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
}
.content{
white-space: normal;
line-height: 1.2;
word-wrap: break-word;
background-color: powderblue;
}
.hide{
display: none;
}
//방법1 - hidden
document.addEventListener('DOMContentLoaded', function () { //DOM 생성 후 이벤트 리스너 등록
// 더보기 버튼 이벤트 리스너
document.querySelector('.btn_open').addEventListener('click', function (e) {
let classList = document.querySelector('.main-video-content').classList; // 더보기 프레임의 클래스 정보 얻기
if (classList.contains('showstep1')) {
classList.remove('showstep1');
document.querySelector('.btn_open').classList.add('hide');
}
//전체보기시 더보기 버튼 감추기 & 감추기 버튼 표시
if (!classList.contains('showstep1') && !classList.contains('showstep2')) {
e.target.classList.add('hide');
document.querySelector('.btn_close').classList.remove('hide');
}
if (classList.contains('showstep2') && !classList.contains('showstep1')) {
document.querySelector('.main-video-content').innerHTML = localStorage.getItem("main-video-content");
}
});
});
document.querySelector('.btn_close').addEventListener('click', function (e) {
e.target.classList.add('hide');
document.querySelector('.btn_open').classList.remove('hide');
document.querySelector('.main-video-content').classList.add('showstep1'); // 초기 감춤 상태로 복귀
});
//컨텐츠 로딩 완료 후 높이 기준으로 클래스 재처리
window.addEventListener('load', function () {
let contentHeight = document.querySelector('.main-video-content > .content').offsetHeight; //컨텐츠 높이 얻기
if (contentHeight <= this.document.querySelector('.main-video-content > .content').style.lineHeight * 4) {
document.querySelector('.main-video-content').classList.remove('showstep1'); // 초기값보다 작으면 전체 컨텐츠 표시
document.querySelector('.btn_open').classList.add('hide'); // 버튼 감춤
}
});
document.addEventListener('DOMContentLoaded', function () { //DOM 생성 후 이벤트 리스너 등록
// 더보기 버튼 이벤트 리스너
document.querySelector('.btn_open').addEventListener('click', function (e) {
let classList = document.querySelector('.main-video-content').classList; // 더보기 프레임의 클래스 정보 얻기
...
if (classList.contains('showstep2') && !classList.contains('showstep1')) {
document.querySelector('.main-video-content > .content').innerHTML = localStorage.getItem("main-video-content");
e.target.classList.add('hide');
document.querySelector('.btn_close').classList.remove('hide');
}
});
//컨텐츠 로딩 완료 후 높이 기준으로 클래스 재처리
window.addEventListener('load', function () {
let contentHeight = document.querySelector('.main-video-content > .content').offsetHeight; //컨텐츠 높이 얻기
if (contentHeight <= this.document.querySelector('.main-video-content > .content').style.lineHeight * 4) {
document.querySelector('.main-video-content').classList.remove('showstep1'); // 초기값보다 작으면 전체 컨텐츠 표시
document.querySelector('.btn_open').classList.add('hide'); // 버튼 감춤
} else {
let main_video_content_short = localStorage.getItem("main-video-content").substring(0, 800) + "...";
document.querySelector('.main-video-content > .content').innerHTML = main_video_content_short;
}
});
fetch()로 데이터 가져오는 법, video 태그 사용법, youtube api 사용법 등을 배워갈 수 있다.
let playlist = document.querySelector(".playlist");
fetch("./data.json")
.then(response => response.json())
.then(data => {
console.log(data);
data = data.playlist;
data.forEach(item => {
console.log(item);
let sub_video = document.createElement('figure');
sub_video.className = "sub-video";
let sub_img = document.createElement('img');
sub_img.src = item.src;
sub_video.appendChild(sub_img);
let sub_desc = document.createElement('figcaption');
let sub_desc_title = document.createElement('div');
sub_desc_title.className = "sub-video-desc-title";
sub_desc_title.textContent = item.title;
sub_desc.appendChild(sub_desc_title);
// let sub_video_desc_content = document.createElement('div');
// sub_desc_content.className = "sub-video-desc-content";
// sub_desc_content.textContent = item.content;
// sub_desc.appendChild(sub_video_desc_content);
sub_video.appendChild(sub_desc);
playlist.appendChild(sub_video);
});
});
{
"playlist": [
{
"src": "http://127.0.0.1:5500/images/first.webp",
"title": " This is title1",
"content": "This is content1"
},
{
"src": "http://127.0.0.1:5500/images/first.webp",
"title": " This is title1",
"content": "This is content1"
},
{
"src": "http://127.0.0.1:5500/images/first.webp",
"title": " This is title1",
"content": "This is content1"
},
{
"src": "http://127.0.0.1:5500/images/first.webp",
"title": " This is title1",
"content": "This is content1"
},
{
"src": "http://127.0.0.1:5500/images/first.webp",
"title": " This is title1",
"content": "This is content1"
},
{
"src": "http://127.0.0.1:5500/images/first.webp",
"title": " This is title1",
"content": "This is content1"
},
{
"src": "http://127.0.0.1:5500/images/first.webp",
"title": " This is title1",
"content": "This is content1"
}
]
}
//detail.js
var player = videojs("player");
player.playlist([
{
sources: [{
src: 'https://www.youtube.com/watch?v=AZGcmvrTX9M',
type: 'video/youtube'
}]
},
{
sources: [{
src: 'https://www.youtube.com/watch?v=n4YXauObskA',
type: 'video/youtube'
}]
},
{
sources: [{
src: 'http://media.w3.org/2010/05/sintel/trailer.mp4',
type: 'video/mp4'
}],
poster: 'http://media.w3.org/2010/05/sintel/poster.png'
}
]);
// Play through the playlist automatically.
player.playlist.autoadvance(0);
player.playlist.repeat(true);
//detail.html
<video id="player" class="video-js" controls autoplay muted preload="auto" width="854" height="480">
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that
<a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video>