이미지에 마우스가 호버되었을 때의 작업은 HTML5에서 추가된 figure 태그와 figcaption 태그를 이용해 처리했다.
figure태그는 삽화, 다이어그램, 사진, 동영상, 음원 등 해당 콘텐츠에 대한 설명을 입력할 때 사용한다.
figcaption는 figure 요소 안에서 콘텐츠에 대한 제목을 출력한다. 반드시 figure 태그 안에서 사용해야한다.
div태그를 사용하도 무방하지만, 웹 접근성때문에 figure태그와 figcaption을 사용했다.
앨범에서 사진을 누르면 상세페이지로 이동하는 이미지 링크형식이였기 때문에, 이를 구현하기 위해 react-router-dom에서 제공하는 Link태그를 사용했다.
a태그를 사용하게 되면 href에 지정된 경로로 페이지 이동을 하면서 새로운 데이터를 다시 불러오게 되지만, Link태그는 SPA방식에 맞게 바뀌어야 하는 화면만 리렌더링 하는 장점이 있다.
button태그와 a태그의 차이점은 다음과 같다.
먼저 a태그는 다른 페이지로 이동하거나, 같은 문서내에 다른 anchor 태그로 이동할때 사용한다. 즉 페이지 이동과 관련된 기능에는 a태그를 사용해야한다.
키보드 엔터로 동작하고, 마우스 오른쪽 버튼을 클릭했을 때 여러 기능들이 존재한다.
button 태그는 이동기능 외에 여러가지 내용의 일부만 변경되는 경우 사용한다.
예를들어 폼을 전송하거나 취소할 때, 팝업이나 모달을 열거나 메뉴를 열때, 동영상을 플레이 할때 등등을 위해 사용한다.
오른쪽 버튼을 클릭 할 시 별다른 기능이 존재하지 않는다.
이처럼 기능상 동일해보이지만, 두개의 태그는 완전히 다른 기능을 가지고 있다. 스크린 리더상에도 다른 용도로 인식하기 때문에 웹 접근성 관점에서도 기능에 따라 올바르게 태그를 마크업하는 과정이 중요하다.
페이지 이동시에는 a태그를 사용해야하고, 폼을 전송하거나 페이지 내에서 사용자와 상호작용시에는 button태그를 사용해야한다.
Grid 형식의 반응형 웹을 구현했기 때문에, 정사각형 이미지 resize 과정에서 최적화 작업이 필요했었다.
초기에는 이미지 컨테이너 사이즈에 따라 이미지 사이즈가 변하도록 구현하였는데, 이렇게 구현하니 컨테이너 사이즈가 변할때마다 이미지 reflow가 너무 많이 발생하였다.
이를 해결하기 위해, img에 width와 aspect-ratio속성으로 치수를 주는 과정을 통해 reflow를 방지하였고, Lazy loading 속성을 적용하였다.
aspect-ratio 는 요소를 크기 비율대로 조정 할 수 있게 해주는 css 속성이다. 최근 반응형 웹 대부분에서 사용한다.
aspect-ratio 속성과 object-fit : cover 속성을 함께 사용하면, 이미지가 깨지지 않으면서 원하는 종횡비를 유지 할 수 있다.
먼저 마크업 작업은 이미지 링크이기때문에 Link태그로 이미지를 감싸고, 그안에 figure, figcaption태그와 img태그를 넣었다.
여기서 figBox는 글씨를 담고있는 배경이고, figcaption은 배경안에 들어가있는 글자이다.
//AlbumPresenter.tsx
import { Link } from 'react-router-dom';
import { AlbumContainer } from './style/AlbumPresenterStyle';
import type { AlbumContent } from '../../../type/AlbumType';
type AlbumPresenterProps = {
albumData: AlbumContent[];
};
const AlbumPresenter = ({ albumData }: AlbumPresenterProps) => {
return (
<AlbumContainer>
{albumData.map((item) => (
<Link to={`${item.albumId}`} className="imgLink" key={item.albumId}>
<figure>
<img src={item.imageUrl} loading="lazy" alt={item.title} />
<div className="figBox">
<figcaption>
<div className="figItemBox">
<img src="/img/HeartDog.svg" alt="HeartDog" className="figItemImg" />
<div>{item.empathyCount}</div>
</div>
<div className="figItemBox">
<img src="/img/Comment.svg" alt="Comment" className="figItemImg" />
<div>{item.commentCount}</div>
</div>
</figcaption>
</div>
</figure>
</Link>
))}
</AlbumContainer>
);
};
export default AlbumPresenter;
마우스가 호버되기 전 상태에서는 figBox는 아무런 배경색이 없고, figcaption의 opacity는 0인 상태이다.
호버되었다면, figBox의 투명도를 0.6으로 배경색을 지정해주고, figcaption의 opacity를 1로 바꿔준다.
figBox에 opacity속성을 지정하게 되면 figcaption의 글씨또한 흐려지기 때문에 rgba를 통해 background color의 투명도를 조정해주는 방법을 사용하였다.
//AlbumPresenterStyle.ts
import styled from 'styled-components';
export const AlbumContainer = styled.section`
margin: min(70px, 5vw) 10vw;
width: 80vw;
height: 100vh;
overflow: auto;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 24px;
img {
border-radius: 8px;
object-fit: cover;
aspect-ratio: 1;
width: 100%;
}
figcaption {
position: absolute;
display: flex;
justify-content: space-evenly;
align-items: center;
opacity: 0;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
border-radius: 8px;
width: 90%;
aspect-ratio: 1;
border: 1px solid ${({ theme }) => theme.color.grayScale.white};
box-sizing: content-box;
}
.figBox {
position: absolute;
top: 0;
left: 0;
border-radius: 8px;
width: 100%;
aspect-ratio: 1;
}
.figItemBox {
display: flex;
flex-direction: column;
align-items: center;
}
.figItemImg {
margin-bottom: 15px;
}
.imgLink {
all: unset;
position: relative;
color: ${({ theme }) => theme.color.grayScale.white};
font-family: ${({ theme }) => theme.font.family.gmarketSans_medium};
font-size: 20px;
aspect-ratio: 1;
&:hover {
cursor: pointer;
.figBox {
background-color: rgba(255, 138, 51, 0.6);
}
figcaption {
opacity: 1;
}
}
}
@media screen and (max-width: 1024px) {
grid-template-columns: repeat(2, 1fr);
&.imgLink {
font-size: 10px;
}
&.figItemImg {
margin-bottom: 10px;
}
}
`;
ref) a태그 vs button 태그 :https://1023labs.com/posts/web-accessibility-btn/
이미지 사이즈 최적화 : https://velog.io/@hustle-dev/%EC%9B%B9-%EC%84%B1%EB%8A%A5%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%B5%9C%EC%A0%81%ED%99%94