스타일 지정된 구성요소와 React 부드러운 스크롤을 사용해서 React웹 사이트를 구축한다.
새로운 프로젝트를 생성한다.
$ npx create-react-app react-website-smooth-scroll
yarn start
로 실행하면 기본 리액트 화면이 나타나는데,프로젝트 제작 불필요한 파일을 제거해준다.
App.css
의 내용을 전부 제거하고 아래 내용을 작성한다.
*{
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Encode Sans Expanded',
sans-serif;
}
components : 페이지 구성하는 컴포넌트가 들어있다.
images : 페이지에 사용되는 이미지 파일이 들어있다.
pages : router를 이용한 페이지가 구현되어있다.
videos :페이지에 사용되는 비디오 영상이 들어있다.
component 폴더 내부에 Navbar폴더를 생성하고 해당 폴더 안에 2개의 파일을 생성한다.
srccomponents/Navbar/index.js
에 어떤 컴포넌트들이 들어갈지 대략적으로 작성한다.
const Navbar = () => {
return (
<>
<Nav>
<NavbarContainer>
<NavLogo>
HEY
</NavLogo>
</NavbarContainer>
</Nav>
</>
);
};
이런식으로 "나는 Navbar라는 컴포넌트를 만들건데, 그 안에 로고를 만들고싶고, 그걸 감싸는 컨테이너도 있었으면 좋겠다" 라는 느낌으로 작성을 한다. 물론 현재 Nav
, NavbarContainer
,NavLogo
라는 컴포넌트를 정의하지 않았기때문에 빨간줄로 오류가 생겼을 것이다.
NavbarElements.js
파일을 코딩한다. styled-components
를 사용해서 js파일 내부에 바로 스타일링을 시작한다.
src/components/Navbar/NavbarElements.js
import styled from "styled-components";
import {Link as LinkR} from 'react-router-dom';
export const Nav = styled.div`
background: #000;
height: 80px;
/* margin-top: -80px; */
display: flex;
justify-content: center;
align-items: center;
font-size: 1rem;
position: sticky;
top: 0;
z-index: 10;
@media screen and(max-width: 960px) {
transition: 0.8s all ease;
}
`;
export const NavbarContainer = styled.div`
display: flex;
justify-content: space-between;
height: 80px;
z-index: 1;
width: 100%;
padding: 0 24px;
max-width: 1100px;
`;
export const NavLogo = styled(LinkR)`
color:red;
justify-content:flex-start ;
cursor:pointer;
font-size: 1.5rem;
display: flex;
align-items: center;
margin-left: 24px;
font-weight: bold;
text-decoration: none;
`
height, display, postion을 설정한다. @media는 단말기의 유형과, 특성이나 수치에 따라 웹사이트나 앱의 스타일을 수정할때 유용하게 사용한다. Link
컴포넌트를 사용할 경우 App.js 내부를 BrowserRouter
로 감싸주어야 한다. BrowserRouter
는 간단히 설명하면 URL과 UI를 동기해주는 <Router>
이다.
현재 상황
src/components/Navbar/index.js
에 컴포넌트를 추가한다.
-MobileIcon : react-icon
에서 가져온 이미지가 들어갈 공간이다.
-FaBars : react-icon
종류중 하나 가로 너비가 768px보다 짧아지는 순간 이 NavMenu 대신 이 아이콘이 나타난다.
-NavMenu : 메뉴 항목 컴포넌트인 NavItem
을 담는 공간이다.
-NavItem : NavMenu
내부에 있는 항목
-NavLinks : NavItem
의 라우터 컴포넌트 클릭하면 하단에 테두리가 생긴다.
<Nav>
<NavbarContainer>
<NavLogo to="/">
LOGO
</NavLogo>
<MoblieIcon>
<FaBars/>
</MoblieIcon>
<NavMenu>
<NavItem>
<NavLinks to="about">About</NavLinks>
</NavItem>
</NavMenu>
</NavbarContainer>
</Nav>
NavBarElements
에도 컴포넌트를 추가로 스타일링 한다.
export const MoblieIcon = styled.div`
display: none;
@media screen and (max-width: 768px){
display:block;
position: absolute;
top: 0;
right: 0;
transform: translate(-100%, 60%);
font-size: 1.8rem;
cursor: pointer;
color: #fff;
}
`
export const NavMenu = styled.ul`
display: flex;
align-items: center;
list-style: none;
text-align: center;
margin-right: -22px;
@media screen and (max-width: 768px) {
display: none;
}
`
export const NavItem = styled.li`
height: 80px;
`
export const NavLinks = styled(LinkS)`
color:#fff ;
display: flex;
align-items: center;
text-decoration: none;
padding: 0 1rem;
height: 100%;
cursor: pointer;
&.active{
border-bottom: 3px solid #01bf71;
}
`
index.js로 돌아가서 컴포넌트들을 import 시킨다.
src/components/Navbar/index.js
/* eslint-disable react/jsx-no-undef */
import React from "react";
import {FaBars} from 'react-icons/fa'
import { MoblieIcon, Nav, NavbarContainer, NavItem, NavLinks, NavLogo, NavMenu } from "./NavbarElements";
const Navbar = () => {
return (
<div>
<Nav>
<NavbarContainer>
<NavLogo to="/">
LOGO
</NavLogo>
<MoblieIcon>
<FaBars/>
</MoblieIcon>
<NavMenu>
<NavItem>
<NavLinks to="about">About</NavLinks>
</NavItem>
</NavMenu>
</NavbarContainer>
</Nav>
</div>
);
};
export default Navbar;
FaBars
는 react-icons
에서 가져온것으로 react-icons
를 설치해야한다.
npm install react-icons --save
NavLinks
는 react-scroll
을 사용하기 때문에 react-scroll
을 설치해야한다.
npm install react-scroll
현재 상황
우측에 NavLinks
가 나타난 것을 확인했다. 화면의 너비가 일정 길이보다 짧아질 경우
우측 상단에 아이콘이 나타난다.
NavMenu
안에 NavItem
을 더 추가했다.
<NavMenu>
<NavItem>
<NavLinks to="about">About</NavLinks>
</NavItem>
<NavItem>
<NavLinks to="Discover">Discover</NavLinks>
</NavItem>
<NavItem>
<NavLinks to="services">Services</NavLinks>
</NavItem>
<NavItem>
<NavLinks to="signup">Sign Up</NavLinks>
</NavItem>
</NavMenu>
현재 상황
NavMenu
옆에 로그인 화면으로 이동하는 버튼을 제작한다. NavBtn
내부에 NavBtnLink
를 생성해서 signin
페이지로 경로를 지정한다.
<NavBtn>
<NavBtnLink to='/signin'>Sign in</NavBtnLink>
</NavBtn>
해당 컴포넌트들을 스타일링한다.
-NavBtn : NavBtnLink를 감싸는 컴포넌트로 너비가 768px보다 좁아지면 사라진다.
-NavBtnLink : 클릭하면 signin
페이지로 이동하는 라우터 컴포넌트. 마우스 커서를 올리면 색이 변경된다.
export const NavBtn = styled.nav`
display: flex;
align-items: center;
@media screen and (max-width: 768px){
display: none;
}
`
export const NavBtnLink = styled(LinkR)`
border-radius: 50px;
background: #01bf71;
white-space : nowrap;
padding: 10px 22px;
color: #010606;
font-size: 16px;
outline: none;
border: none;
cursor: pointer;
transition: all 0.2s ease-in-out;
text-decoration: none;
&:hover {
transition: all 0.2s ease-in-out;
background: #fff;
color: #010606;
}
`
이전의 컴포넌트들처럼, index.js
에 import시켜주고 실행해보자.
현재 상황
Sidebar 컴포넌트를 제작한다.
compnents
폴더 내부에 Sidebar
폴더를 생성하고 index.js
, SidebarElements.js
를 생성한다.
index.js
const Sidebar = () => {
return (
<>
<SidebarContainer>
<Icon>
<CloseIcon/>
</Icon>
</SidebarContainer>
</>
)
}
SidebarContainer
: MobileIcon
에 있는 FaBar
를 클릭하면 나타나는 Sidemenu 창의 레이아웃
Icon
: SideMenu 화면
CloseIcon
: SideMenu를 닫는 버튼
SidebarElements.js
에 해당 컴포넌트들을 스타일링한뒤 export 한다.
import styled from 'styled-components';
import {FaTimes} from 'react-icons/fa';
export const SidebarContainer = styled.aside`
position: fixed;
z-index: 999;
width: 100%;
height: 100%;
display: grid;
align-items: center;
top: 0;
left: 0;
transition: 0.3s ease-in-out;
opacity: ${({isOpen}) => (isOpen ? '100%' : '0')};
top : ${({isOpen}) => (isOpen ? '0' : '-100%')};
`;
export const CloseIcon = styled(FaTimes)`
color: #fff
`;
export const Icon = styled.div`
position: absolute;
top: 1.2rem;
right: 1.5rem;
background: transparent;
font-size: 2rem;
cursor: pointer;
outline: none;
`
App.js
과 src/components/Sidebar/index.js
에서 import 시킨다.
function App() {
return (
<Router>
<Sidebar/>
<Navbar/>
</Router>
);
}
/* eslint-disable react/jsx-no-undef */
import React from 'react'
import { CloseIcon, Icon, SidebarContainer } from './SidebarElements'
const Sidebar = () => {
return (
<>
<SidebarContainer>
<Icon>
<CloseIcon/>
</Icon>
</SidebarContainer>
</>
)
}
export default Sidebar
`
실행을 시켜도 현재는 기본 화면과 같지만, isOpen
조건문을 주석처리하면 아래와 같이 나타난다.
우측 상단에 react-icons
에서 가져온 FaTimes
아이콘이 나타난다.
사용자가 MobileIcon
을 클릭 하면 위의 사이드메뉴가 나타나고, NavBar
의 NavItem
항목들이 나타나야한다. 따라서 해당 Item들을 SideBar에서도 작성한다.
const Sidebar = () => {
return (
<>
<SidebarContainer>
<Icon>
<CloseIcon/>
</Icon>
<SidebarWrapper>
<SidebarMenu>
<SidebarLink to="about">
About
</SidebarLink>
<SidebarLink to="discover">
Discover
</SidebarLink>
<SidebarLink to="services">
Services
</SidebarLink>
<SidebarLink to="signup">
Sign Up
</SidebarLink>
</SidebarMenu>
<SideBtnWrap>
<SidebarRoute to="/singin">Sign In</SidebarRoute>
</SideBtnWrap>
</SidebarWrapper>
</SidebarContainer>
</>
)
}
SidebarWrapper
: 사이드바 메뉴 와 회원가입 버튼을 표시하는 외부 wrapper
SidebarMenu
: 사이드바 메뉴 항목을 담아놓은 ul
SidebarBtnWrap
: 회원가입 버튼 레이아웃
SidebarRoute
: 회원가입 버튼. 누르면 signin
페이지로 이동한다.
SidebarElements.js
에서 추가로 스타일링을 작성한다.
export const SidebarWrapper = styled.div`
color:#fff;
`
export const SidebarMenu = styled.ul`
display: grid;
grid-template-columns: 1f;
grid-template-rows: repeat(6, 80px);
text-align: center;
@media screen and (max-width: 480px){
grid-template-rows: repeat()(6, 60px);
}
`
export const SidebarLink = styled(LinkS)`
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
text-decoration: none;
list-style: none;
transition: 0.2s ease-in-out;
color: #fff;
cursor: pointer;
&:hover{
color: #01bf71;
transition: 0.2s ease-in-out;
}
`
export const SideBtnWrap = styled.div`
display: flex;
justify-content:center;
`
export const SidebarRoute = styled(LinkR)`
border-radius: 50px;
background: #01bf71;
white-space: nowrap;
padding: 16px 64px;
color: #010606;
font-size: 16px;
outline: none;
border: none;
cursor: pointer;
transition: all 0.2s ease-in-out;
text-decoration: none;
&:hover{
transition: all 0.2s ease-in-out;
background: #fff;
color: #010606;
}
`
src/components/Sidebar/index.js
에 컴포넌트들을 추가로 import 한다.
/* eslint-disable react/jsx-no-undef */
import React from 'react'
import { CloseIcon, Icon, SidebarContainer, SidebarLink, SidebarMenu, SidebarRoute, SidebarWrapper, SideBtnWrap } from './SidebarElements'
const Sidebar = () => {
return (
<>
<SidebarContainer>
<Icon>
<CloseIcon/>
</Icon>
<SidebarWrapper>
<SidebarMenu>
<SidebarLink to="about">
About
</SidebarLink>
<SidebarLink to="discover">
Discover
</SidebarLink>
<SidebarLink to="services">
Services
</SidebarLink>
<SidebarLink to="signup">
Sign Up
</SidebarLink>
</SidebarMenu>
<SideBtnWrap>
<SidebarRoute to="/singin">Sign In</SidebarRoute>
</SideBtnWrap>
</SidebarWrapper>
</SidebarContainer>
</>
)
}
export default Sidebar
현재상황
SideBar의 메뉴 항목과 아래에 회원가입 버튼이 생성되었다.
App.js에 현재 Sidebar
와 NavBar
컴포넌트가 있는데 이 둘을 합친 Home
이라는 컴포넌트를 생성한다.
src/pages
에 index.js
파일을 생성한다.
import React from 'react'
import Navbar from '../components/Navbar'
import Sidebar from '../components/Sidebar'
const Home = () => {
return (
<>
<Sidebar/>
<Navbar/>
</>
)
}
export default Home
그리고 App
에 Home
컴포넌트를 불러와서 수정한다.
function App() {
return (
<Router>
<Home/>
</Router>
);
}
MobileIcon
을 클릭했을때 사이드 메뉴가 나타나게끔 function을 작성할 것이다.
src/pages/index.js
에 useState
를 이용하여 isOpen
의 상태를 관리할 것이다.
const [isOpen,setIsOpen] = useState(false)
const toggle = () => {
setIsOpen(!isOpen)
}
이렇게만 쓸게 아니라 해당 컴포넌트에 필요한 props들을 넘겨줘야 한다. 왜냐하면 Sidebar
를 스타일링 할 때, isOpen의 값에 따라 나타나게 설정했고 , CloseIcon
을 터치하면 사라지게 해주는 함수를 받아와야하기 때문이다.
따라서 Navbar
컴포넌트에는 toggle
을, Sidebar
컴포넌트에는 isOpen
과 toggle
을 보내줘야한다.
return (
<>
<Navbar toggle={toggle}/>
<Sidebar isOpen={isOpen} toggle={toggle}/>
</>
)
받은 props들을 이용하여 해당 컴포넌트에 알맞는 위치에 사용되게끔 toggle, isOpen을 배치한다.
Navbar/index
<MoblieIcon onClick={toggle}>
<FaBars/>
</MoblieIcon>
Sidebar/index
<SidebarContainer isOpen={isOpen} onClick={toggle} >
<Icon onClick={toggle}>
<CloseIcon/>
</Icon>
현재상황
MobileIcon
을 클릭하면 사이드메뉴가 나타나고 CloceIcon
혹은 SideBarContainer
내부 요소를 제외한 공간을 터치하면 다시 사라지게 된다.
사이드 메뉴중 하나를 클릭하면 해당 내용으로 이동하게 하려면 먼저 사이드 메뉴창이 닫혀야 하기 때문에 SidebarLink
에도 toggle
props 를 추가한다.
<SidebarMenu>
<SidebarLink to="about" onClick={toggle}>
About
</SidebarLink>
<SidebarLink to="discover" onClick={toggle}>
Discover
</SidebarLink>
<SidebarLink to="services" onClick={toggle}>
Services
</SidebarLink>
<SidebarLink to="signup" onClick={toggle}>
Sign Up
</SidebarLink>
</SidebarMenu>