들어가며,
회사에서 디자인 시안에 따라 개발을 하던 중, 탭 메뉴를 눌렀을 때 background-image를 다르게 보여줘야 했다.
컴포넌트에 배경이미지를 적용하면 컴포넌트 내, 즉 컴포넌트 크기만큼만 사용할 수 있기 때문에 전체너비를 다 차지하지 못한다.
어떻게 해야할 지 고민을 하던 와중, 탭을 눌렀을 때 변하는 상태에 따라서 배경이미지 css를 조절하면 어떨까라는 생각에 도달했다.
본문
먼저 가장 부모 컴포넌트를 만들어준다.
가장 바깥 부모에서 상태관리를 해준 이유는 가장 바깥 컴포넌트에 배경이미지를 적용해야하기 때문이다.
위에서 말했듯이, 전체 너비에 배경이미지를 적용하기 위해선 만들어낸 컴포넌트가 아닌 가장 바깥 컴포넌트에 적용한다.
// App.jsx
import React, { useState } from "react";
import Tab from "./Tab";
import "./App.css";
const App = () => {
// 탭을 했는지 유무에 따른 상태관리
const [selectedTab, setSelectTab] = useState("이미지1번");
return (
// 상태가 어떤지에 따라 css className을 지정한다.
// 만약 이미지 1번이라면 .img1이 붙은 스타일링이 렌더링되고, 아니면 .img2의 스타일링이 적용된다.
<section
className={`container ${selectedTab === "이미지1번" ? "img1" : "img2"}`}
>
<div className="aboutContents">
<div className="titleBox">
<span className="badge">테스트</span>
<h2 className="title">배경이미지 상태따라 렌더링하기</h2>
</div>
// Tab 컴포넌트에 상태를 넘겨준다.
// 필자는 처음에 selectedTab을 넘겨주지 않아서 Tab의 자식 컴포넌트들이 렌더링 되지 않았다..!
<Tab selectedTab={selectedTab} setSelectTab={setSelectTab} />
</div>
</section>
);
};
export default App;
그리고 위의 코드에 따라 적용해야하는 스타일링이다.
container 라는 class를 가진 태그 중 추가적으로 background-image 스타일링을 넣은 class를 만들어준다.
이미지 1번 탭에 적용시킬 img1 클래스와 이미지 2번 탭에 적용시킬 img2 클래스를 만들어준 후,
조건문에 따라 동적으로 클래스가 들어가도록 만들어준다.
즉, 이미지 1번 탭을 눌렀다면 img1 클래스를 가진 스타일링이 적용되고
이미지 2번 탭을 눌렀다면 img2 클래스를 가진 스타일링이 동적으로 적용된다.
높이 1200px은 이미지가 보이도록 임의로 준 것이므로 사용하는 곳에 맞게 적용시키면 된다.
/* 가장 바깥 컴포넌트 중 img1 클래스를 가진 태그에 스타일링 적용 */
.container.img1 {
background-image: url("../src/assets/bg_01.jpeg");
background-repeat: no-repeat;
background-size: cover;
height: 1200px;
}
/* 가장 바깥 컴포넌트 중 img2 클래스를 가진 태그에 스타일링 적용 */
.container.img2 {
background-image: url("../src/assets/bg_02.jpeg");
background-repeat: no-repeat;
background-size: cover;
height: 1200px;
}
그리고 Tab 컴포넌트를 만들어준다.
이 컴포넌트에서는 탭을 눌렀을 때 (이미지 1번 탭을 눌렀는지? 이미지 2번 탭을 눌렀는지) 따라서 렌더링 되는 내용이 달라진다.
Tab.jsx
import React from "react";
// 부모에서 내려준 상태들을 받아준다.
const Tab = ({ selectedTab, setSelectTab }) => {
// 탭 메뉴를 클릭했을 때 실행하는 함수
const handleTabClick = (tab) => {
setSelectTab(tab);
};
// 탭을 눌렀을 때 나와야하는 내용들을 조건문에 따라 출력한다.
// selectedTab이 이미지1번 탭이라면, 그에 맞는 내용을 출력하는 것.
const getContentByTab = () => {
if (selectedTab === "이미지1번") {
return <div className="imgText">알록달록 배경이미지 입니다.</div>;
} else if (selectedTab === "이미지2번") {
return <div className="imgText">귀여운 완전 큰 고양이 입니다.</div>;
}
};
return (
<div>
<ul className="list">
// 이미지 1번을 선택했다면, 그에 맞는 css를 보여준다.
<li
className={`tab ${selectedTab === "이미지1번" ? "selected" : ""}`}
onClick={() => handleTabClick("이미지1번")}
>
이미지 1번 - tab1
</li>
<li
// 이미지 2번을 선택했다면, 그에 맞는 css를 보여준다.
className={`tab ${selectedTab === "이미지2번" ? "selected" : ""}`}
onClick={() => handleTabClick("이미지2번")}
>
이미지 2번 - tab2
</li>
</ul>
// 탭 메뉴에 따른 내용을 출력하는 함수 코드
{getContentByTab()}
</div>
);
};
export default Tab;
Tab 컴포넌트를 추가적으로 넣은 이유는, 부모에서 넘겨준 상태들이 어떤식으로 사용되는지 보면 좋을 것 같아서 추가적으로 넣었다.
탭을 클릭했을 때 handleTabClick 함수를 실행시키고 그에 맞는 tab 스타일링을 넣어준다.
여기서 tab 스타일링은 탭 메뉴 색을 변경시킨 것이다.
.tab {
cursor: pointer;
color: #2a6598;
font-weight: 600;
margin-right: 30px;
}
/* 탭이 선택되었을 때 폰트 색상 변경 */
.tab.selected {
color: forestgreen;
font-weight: 600;
}
결과 화면
이처럼 이미지 1번을 눌렀을 때와 이미지 2번을 눌렀을 때 배경이미지가 변경된다.
만약 개발을 하는데 디자인 시안에서 이미지가 컴포넌트 너비 밖으로 꽉 차게 적용되어야 한다면 이런식으로 이용해보자.
마치며,
예전에 팀 프로젝트를 했을 당시 우리 팀은 아니고 다른 팀 동료가 이 문제에 대해서 물어본 적이 있었다.
디자인 시안에 따르면 전체 화면에 적용해야 하는데, 지금 내가 만들고 있는 컴포넌트 너비는 내용에 따라 크기가 채워지기 때문에 배경이미지를 적용했을 때 그 크기만큼만 적용되는 문제가 있었다.
그때도 해결하지 못하고 고민하다가 넘어갔었는데 이번 업무에서 이러한 상황을 또 마주하게 됐다.
처음에는 자식 컴포넌트에서 하나의 wrapper를 더 만들어 width를 100vw로 주고 적용시킬까 했지만 다른 이미지도 있어 무너지고, 원하는대로 잘 적용되지 않았다.
그러다가 탭 상태가 변하는 것에 따라서 적용해주면 어떨까?라는 생각을 하게 되었고 처음에는 실패했다. (밑의 자식 컴포넌트가 안나오는 문제가 발생했다.)
이 문제를 생각보다 오래 고민했는데 아주 사소한 곳에서 잘못이 있었다..
selctedTab도 넘겨주어야 하는데 그게 넘어가지 않아서 자식 컴포넌트들이 렌더링되지 않는 문제가 발생했었다. Tab 컴포넌트에서 보듯, 자식컴포넌트에서도 selectedTab을 사용하고 있는데 내가 전달해주지 않아서 렌더링이 되지 않았던 것이다. 사소한 곳에서 문제가 발생하는게 찾기 은근히 힘든 것 같다.
아무튼, 예전부터 고민하고 있었던 부분인데 예상치 못한 곳에서 다시 마주했고 해결까지 해서 뿌듯했다.
생각보다 많은 곳에서 쓰일 수 있는 방법이라 블로그에 정리해둘겸 공유하고자 글을 남긴다.