제작 기간: 2023.07 ~ 2023.07 기여도: 퍼블리싱 100% 사용 기술: React, TypeScript, SCSS URL: https://craftcharter.vercel.app/
craftcharter 사이트의 메인 페이지, Contact 페이지를 React.js로 클론 코딩하였습니다. 윈도우 스크롤 api와 CSS Animation을 이용하여 모션 및 인터랙션을 구현하였습니다.
// Main.tsx
import Home from 'components/home/Home';
import Fleet from 'components/fleet/Fleet';
import Service from 'components/service/Service';
import About from 'components/about/About';
import Contact from 'components/contact/Contact';
const Main = () => {
return (
<>
<Home />
<Fleet />
<Service />
<About />
<Contact />
</>
);
};
export default Main;
// footer.tsx
const Footer = () => {
return (
<footer className='Footer'>
<div className='Footer__header'>
<Logo fill='#000' /> // 다른 섹션, 컴포넌트에서도 재사용할 수 있는 UI는 컴포넌트화하여 사용
<nav className='Footer__nav'>
<ul>
{footerMenu.map((el, idx) => {
return (
<li className='Footer__nav-item' key={`ftmenu${idx}`}>
<a href={el.link}>{el.title}</a>
</li>
);
})}
</ul>
<Link to='/contact' className='Footer__btn--round'>
contact
</Link>
</nav>
</div>
<hr />
<div className='Footer__bottom'>
<div className='Footer__link-container'>
<Social /> // 다른 곳에서도 사용되는 컴포넌트
<Credit />
</div>
<ul className='Footer__info'>
{infoMenu.map((el, idx) => {
return (
<li className='Footer__info-item' key={`ftinfo${idx}`}>
<a href={el.link}>{el.title}</a>
</li>
);
})}
<li className='Footer__info-copy'>© 2023 Craft</li>
</ul>
</div>
</footer>
);
};
export default Footer;
기본적으로 섹션 컴포넌트를 만들어 섹션 별 마크업 확인이 용이하도록 작성하고, 재사용이 가능한 UI는 컴포넌트화하여 중복 코드 작성을 줄이고자 하였습니다.
// App.tsx
function App() {
const [isOpenCtaPop, setIsOpenCtaPop] = useState(false);
// Cta 팝업은 전역 상태로 분리하여 여러 하위 컴포넌트에서도 접근 및 상태 변경이 가능하도록 하였습니다.
return (
<CtaPopContext.Provider value={{ isOpenCtaPop, setIsOpenCtaPop }}>
<div className='Wrapper'>
<BrowserRouter>
<Header />
<Routes>
<Route path='/' element={<Main />} />
<Route path='/contact' element={<ContactPage />} />
</Routes>
<Footer />
<SideCta />
<SideCtaPop />
</BrowserRouter>
</div>
</CtaPopContext.Provider>
);
}
메인 페이지/Contact 페이지 간의 전환을 위해 react-router
라이브러리를 사용하였습니다.
사이드의 ‘TAKE FLIGHT’ 버튼을 클릭하면 cta 팝업이 출력되는데, 모바일 메뉴 컴포넌트에도 같은 기능을 하는 버튼이 있기 때문에 react의 Context API
를 이용하여 전역 상태로 따로 빼주었습니다.
// NOTE font
@mixin exo($weight: 400) {
font-family: 'Exo 2', sans-serif;
font-weight: $weight;
letter-spacing: 0.02em;
word-spacing: 0.04em;
}
@mixin flex($jc: flex-start, $ai: stretch, $gap: 0, $dir: row) {
display: flex;
justify-content: $jc;
align-items: $ai;
gap: $gap;
flex-direction: $dir;
}
@mixin btn-with-arrow($color: $black) {
@include flex(flex-start, center);
@include exo(600);
display: inline-flex;
cursor: pointer;
font-size: 1.6rem;
color: $color;
white-space: nowrap;
&::after {
content: url('../assets/image/common/btn-arrow.svg');
width: 2rem;
display: inline-block;
margin-left: 2rem;
transition: transform 0.3s;
}
&:hover::after {
transform: translateX(0.6rem);
}
}
@mixin btn-rounded($color: $white, $hover: $black) {
@include exo(400);
@include btn-with-arrow($color);
padding: 0 3rem;
height: 4rem;
border: 0.1rem solid $color;
background: transparent;
transition:
background 0.3s,
color 0.3s;
border-radius: 10rem;
&:hover {
background: $color;
color: $hover;
}
&::after {
display: none;
}
}
공통적으로 여러 곳에 쓰이는 폰트, 버튼 스타일의 경우 SCSS의 mixin 기능을 이용하여 중복을 줄이고 확장성을 높이고자 하였습니다.