๊ฐœ๋ฐœ ๊ณต๋ถ€/๋ฐ๋ธŒ์ฝ”์Šค TIL

[ํด๋ผ์šฐ๋”ฉ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์—”์ง€๋‹ˆ์–ด๋ง TIL] 240123 - ๊ณ ์–‘์ด ์‚ฌ์ง„ ๊ฒ€์ƒ‰ ์‹ค์Šต 6

๊ฐ€์šค์ด 2024. 1. 23. 22:43

Intro


์ถ”๊ฐ€ ์š”๊ตฌ ์‚ฌํ•ญ ํ•ด๊ฒฐ ํ•˜๊ธฐ์— ๋Œ€ํ•œ ๊ฐ•์˜๋ฅผ ๋“ค์—ˆ๋‹ค.

์ „๋‚  ๋ชจ์˜ํ…Œ์ŠคํŠธ์—์„œ ํ’€์—ˆ๋˜ ๋งˆ์šฐ์Šค ์˜ค๋ฒ„ ๋ฌธ์ œ๋ฅผ ๋‚˜๋Š” ์ด๋ฒคํŠธ ํ—จ๋“ค๋Ÿฌ๋กœ ๊ตฌํ˜„ํ–ˆ๋Š”๋ฐ ๊ฐ•์‚ฌ๋‹˜์€ CSS๋กœ ํ›จ์”ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋‹ค.

 

 

 

์˜ค๋Š˜ ํ•™์Šตํ•œ ๋‚ด์šฉ



๐Ÿ‘ฉ‍๐Ÿ’ป ๊ฒ€์ƒ‰์–ด ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ˆจ๊ฒผ๋‹ค๊ฐ€ ์ธํ’‹์— ๋งˆ์šฐ์Šค๊ฐ€ ์˜ค๋ฒ„๋  ๋•Œ ๋ณด์—ฌ์ฃผ๋„๋ก UI๋ฅผ ๋ฐ”๊ฟ”์ฃผ์„ธ์š”.
  • style.css
.SearchResult .item .content {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.3);
  color: #fff;
  text-align: center;
  font-size: 30px;
  opacity: 0;
  transition: all 0.5s ease;
}

.SearchResult .item .content:hover {
  opacity: 1;
}

 

์š”์†Œ๋ฅผ ํˆฌ๋ช…ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” opacity ์†์„ฑ์„ ์ด์šฉํ•ด ์ฒ˜์Œ์—” ํˆฌ๋ช…์ด์—ˆ๋‹ค๊ฐ€ ๋งˆ์šฐ์Šค๊ฐ€ ์˜ค๋ฒ„๋˜์—ˆ์„ ๋•Œ opacity๋ฅผ 1๋กœ ๋ฐ”๊ฟ” ํ™”๋ฉด์— ๋…ธ์ถœ๋˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋…ธ์ถœ๋˜๊ธฐ ์œ„ํ•ด transition ์†์„ฑ๋„ ์ ์šฉํ•˜์˜€๋‹ค.

์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด position: absolute; ๋ฅผ ํ™œ์šฉํ•ด์„œ ์œ„์น˜๋ฅผ ์ ์ ˆํžˆ ๋ฐฐ์น˜ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

JS์—์„œ mouseover/out ์ด๋ฒคํŠธ๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•˜๊ณ  ๋ช…ํ™•ํ•˜๋‹ค.

์ „๋‚  TIL์—์„œ ๋ดค๋“ฏ์ด mouseover/out ์ด๋ฒคํŠธ๋ฅผ ์ ์šฉํ•˜๋ฉด ์ฝ”๋“œ๋„ ํ›จ์”ฌ ๊ธธ์–ด์ง€๊ณ  ์ง๊ด€์ ์ด์ง€ ์•Š์•„ ์ฝ”๋“œ ๋ณด๊ธฐ ๋ถˆํŽธํ•˜๋‹ค.

CSS๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฑด ๋จผ์ € CSS๋ฅผ ํ™œ์šฉํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์—ฌ์•ผ๊ฒ ๋‹ค.

 

 


๐Ÿ‘ฉ‍๐Ÿ’ป ๋ชจ๋‹ฌ ์—ด๊ณ  ๋‹ซ๊ธฐ์— fade in/out์„ ์ ์šฉํ•ด ์ฃผ์„ธ์š”.

 

  • style.css
.ImageInfo {
  position: fixed;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.5);
  opacity: 0;
  visibility: hidden;
  transition: all 0.3s ease;
}

.ImageInfo.show {
  visibility: visible;
  opacity: 1;
}

 

  • ImageInfo.js
setState(nextData) {
  this.data = nextData;
  this.render();
  this.setFade(nextData.visible);
}

setFade(visible) {
  if (visible) this.$imageInfo.classList.add('show');
  else this.$imageInfo.classList.remove('show');
}

 

 

 


๐Ÿ‘ฉ‍๐Ÿ’ป ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๊ฐ ์•„์ดํ…œ์— ๋งˆ์šฐ์Šค ์˜ค๋ฒ„์‹œ ๊ณ ์–‘์ด ์ด๋ฆ„์„ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

 

์–ด์ œ ํ’€์—ˆ๋˜ ๋ฌธ์ œ!!

CSS์˜ :hover ์†์„ฑ์œผ๋กœ ์•„์ฃผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋‹ค์‹œ ๊ตฌํ˜„ํ–ˆ๋‹ค.

์œ„์˜ ๊ฒ€์ƒ‰์–ด ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์ˆจ๊ฒผ๋‹ค๊ฐ€ ์ธํ’‹์— ๋งˆ์šฐ์Šค๊ฐ€ ์˜ค๋ฒ„๋  ๋•Œ ๋ณด์—ฌ์ฃผ๋„๋ก UI๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฌธ์ œ์—์„œ ํ•ด๊ฒฐํ•œ ๋ฐฉ๋ฒ•๋Œ€๋กœ ์ง„ํ–‰ํ–ˆ๋‹ค.

.SearchResult .item {
  position: relative;
  background-color: #eee;
  display: inline-block;
  margin: 0 0 1em;
  width: 100%;
  cursor: pointer;
}

.SearchResult .item .content {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.3);
  color: #fff;
  text-align: center;
  font-size: 30px;
  opacity: 0;
  transition: all 0.5s ease;
}

.SearchResult .item .content:hover {
  opacity: 1;
}

 

content ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ง„ div ์š”์†Œ์— ๊ณ ์–‘์ด ์ด๋ฆ„์„ ํ‘œํ˜„ํ•˜์˜€๋Š”๋ฐ width, height๋ฅผ 100%๋กœ ์„ค์ •ํ•œ ๋’ค top: 50%์œผ๋กœ ์ง€์ •ํ•˜๋ฉด ์ด๋ฏธ์ง€ ๊ฐ€์šด๋ฐ์— ๊ธ€์ž๊ฐ€ ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

 

 


๐Ÿ‘ฉ‍๐Ÿ’ป ์ตœ๋Œ€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๊ฐฏ์ˆ˜๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” select UI๋ฅผ ์ œ๊ณตํ•ด ์ฃผ์„ธ์š”.
option์€ 10๊ฐœ, 25๊ฐœ, 50๊ฐœ ์ž…๋‹ˆ๋‹ค. (api๋Š” 50๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์ง€์›ํ•˜๋ฏ€๋กœ BE ์ˆ˜์ • ํ•„์š”)

 

select ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ option ๊ฐ’์— ๋”ฐ๋ผ data๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค.

  • api.js
const api = {

  ...
  
  fetchCatsWithLimit: (keyword, limit) => {
    return request(`${API_ENDPOINT}/api/cats/search?q=${keyword}&limit=${limit}`);
  },
  
  ...
}
  • SearchInput.js
...
    // select UI
    const $limitCount = document.createElement('select');
    this.$limitCount = $limitCount;
    this.$limitCount.classList = 'LimitCount';

    $wrapper.appendChild(this.$limitCount);

    const LimitCountOptions = [10, 25, 50];
    LimitCountOptions.map((option) => {
      let $option = document.createElement('option');
      $option.value = option;
      $option.textContent = `${option}๊ฐœ`;

      $limitCount.appendChild($option);
    });

...

 

onSearch์— ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๊ธฐ์กด์— keyword ํ•˜๋‚˜๋งŒ ๋ฐ›์•˜๋Š”๋ฐ limit์„ ์ถ”๊ฐ€๋กœ ๋ฐ›์•„ api๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค.

 

 

 


๐Ÿ‘ฉ‍๐Ÿ’ป ๋žœ๋ค ๊ณ ์–‘์ด ๋ฐฐ๋„ˆ ์„น์…˜ ์ถ”๊ฐ€ํ˜„์žฌ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๋ชฉ๋ก ์œ„์— ๋ฐฐ๋„ˆ ํ˜•ํƒœ์˜ ๋žœ๋ค ๊ณ ์–‘์ด ์„น์…˜์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
์•ฑ์ด ๊ตฌ๋™๋  ๋•Œ /api/cats/random50 api๋ฅผ ์š”์ฒญํ•˜์—ฌ ๋ฐ›๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ณ„๋„์˜ ์„น์…˜์— ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ๋งŽ๋”๋ผ๋„ ํ™”๋ฉด์— 5๊ฐœ๋งŒ ๋…ธ์ถœํ•˜๋ฉฐ ๊ฐ ์ด๋ฏธ์ง€๋Š” ์ขŒ, ์šฐ ์Šฌ๋ผ์ด๋“œ ์ด๋™ ๋ฒ„ํŠผ์„ ๊ฐ–์Šต๋‹ˆ๋‹ค.
์ขŒ, ์šฐ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด, ํ˜„์žฌ ๋…ธ์ถœ๋œ ์ด๋ฏธ์ง€๋Š” ์‚ฌ๋ผ์ง€๊ณ  ์ด์ „ ๋˜๋Š” ๋‹ค์Œ ์ด๋ฏธ์ง€๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. (ํŠธ๋ Œ์ง€์…˜์€ ์„ ํƒ)

 

๋ฐฐ๋„ˆ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” CSS ์Šคํ‚ฌ(?)์ด ํ•„์š”ํ•˜๋‹ค.

Banner.js ์ฝ”๋“œ์—์„œ ๊ตฌํ˜„ํ•ด์•ผ ํ•  ๊ฒƒ์€..

1. banner ์š”์†Œ์˜ width ํฌ๊ธฐ๋ฅผ (๋ฐฐ๋„ˆ ํฌ๊ธฐ * ๋ฐฐ๋„ˆ์— ๋ณด์—ฌ ์ค„ ์ด๋ฏธ์ง€ ๊ฐฏ์ˆ˜)๋กœ ์ง€์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

this.$banner.style.width = Number(this.$wrapper.clientWidth) * this.data.length + 'px';
this.$banner.querySelectorAll('li').forEach((item) => {
  item.style.width = this.$wrapper.clientWidth + 'px';
});

 

2. css์—์„  overflow: hidden ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค˜์•ผ ํ•จ + float: left๋กœ ๋ฐฐ๋„ˆ ์ด๋ฏธ์ง€์ธ li ์š”์†Œ๋“ค์„ ์™ผ์ชฝ์—์„œ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ๋ถ™์—ฌ์ค€๋‹ค.

.Banner {
  width: 100%;
  height: 300px;
  overflow: hidden;
  padding: 0;
  margin: 0;
  position: relative;
}

.Banner ul {
  margin: 0;
  padding: 0;
  position: absolute;
  transition: all 0.3s ease;
}

.Banner ul li {
  float: left;
  margin: 0;
  padding: 0;
  list-style: none;
  height: 300px;
  background-position: 50% 50%;
  background-size: cover;
}

.Banner button {
  position: absolute;
  top: 50%;
  z-index: 10;
  cursor: pointer;
}

.Banner .prevBtn {
  left: 10px;
}

.Banner .nextBtn {
  right: 10px;
}

 

3. prev/next button ์š”์†Œ๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ๋ฐฐ๋„ˆ ์ด๋ฏธ์ง€๊ฐ€ ๋„˜์–ด๊ฐ€๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ

changeCurrent(index) {
  this.current = index;
  this.moveTo(index);
}

moveTo(index) {
  let leftPosition = -Number(this.$wrapper.clientWidth) * index;
  this.$banner.style.left = leftPosition + 'px';
}
this.$prevButton.addEventListener('click', (e) => {
  let prev = this.current - 1;
  if (prev === -1) return;
  this.changeCurrent(prev);
});

this.$nextButton.addEventListener('click', (e) => {
  let next = this.current + 1;
  if (next === this.data.length) return;
  this.changeCurrent(next);
});

 

 

 

 

๋งˆ๋ฌด๋ฆฌ


ํฌ๊ฒŒ ์–ด๋ ค์šด ๋‚ด์šฉ์€ ์—†์—ˆ์ง€๋งŒ ํ•˜๋‹ค๋ณด๋ฉด CSS๊ฐ€ ์˜คํžˆ๋ ค ๋ณต์žกํ•ด์งˆ ๊ฒƒ ๊ฐ™์€ ๋А๋‚Œ์ด๋‹ค...

๋ฐฐ๋„ˆ UI๋„ ์›นํŽ˜์ด์ง€ ๊ฐœ๋ฐœ์— ๊ฑฐ์˜ ํ•„์ˆ˜์ธ๋ฐ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ๋˜ ๊ณต๋ถ€ํ•ด๋ด์•ผ๊ฒ ๋‹ค.

๊ณต๋ถ€ํ•  ๊ฒŒ ๋„ˆ๋ฌด ๋„ˆ๋ฌด ๋„ˆ๋ฌด ๋งŽ๋‹ค.