포트폴리오 사이트 만들기 - React
소개
안녕하세요! 웹스토리보이입니다. 이제 하나씩 컴퍼넌트를 완성해 가봅시다. 7개의 컴포넌트를 하나씩 완성하면 기존처럼 멋있는 사이트가 완성될 것입니다. 처음에 만들었던 방식을 토대로 변화하는 과정과 재사용 및 효율적인 코딩방식으로 변화는 과정을 눈여겨 보면 좋습니다. 그럼 시작해보겠습니다.
인덱스
VITE SITE
- 1. 셋팅하기
- 1_1. vite 설치하기
- 1_2. vite 폴더 정리하기
- 1_3. gsap/lenis 설치하기
- 1_4. git 연동하기
- 2. 레이아웃
- 2.1 레이아웃 구조 만들기
- 2.2 메인 레이아웃 구조 만들기
- 2.3 CSS 셋팅하기
- 2.4 JavaScript 셋팅하기
- 3. 헤더 영역
- 3.1 헤더 구조 잡기
- 3.2 헤더 디자인 설정
- 3.3 반응형 작업하기
- 3.4 메뉴 자바스크립트 설정
- 4. 인트로 영역
- 4.1 인트로 구조 잡기
- 4.2 인트로 디자인 설정
- 4.3 반응형 작업하기
- 5. 스킬 영역
- 5.1 스킬 구조 잡기
- 5.2 스킬 디자인 설정
- 5.3 반응형 작업하기
- 6. 사이트 영역
- 6.1 사이트 구조 잡기
- 6.2 사이트 디자인 설정
- 6.3 반응형 작업하기
- 7. 포트폴리오 영역
- 7.1 사이트 구조 잡기
- 7.2 사이트 디자인 설정
- 7.3 반응형 작업하기
- 7.4 스크립트 작업하기
- 8. 연락처 영역
- 8.1 연락처 구조 잡기
- 8.2 연락처 디자인 설정
- 8.3 반응형 작업하기
- 9. 푸터 영역
- 9.1 푸터 구조 잡기
- 9.2 푸터 디자인 설정
- 9.3 반응형 작업하기
- 10. 마무리
- 10.1 스무스 효과주기
- 10.2 링크 연결하기
- 10.3 netlify에 배포하기
REACT SITE
- 1. 셋팅하기
- 1_1. React 설치하기
- 1_2. React 폴더 정리하기
- 1_3. 라이브러리 설치하기
- 1_4. git 연동하기
- 2. 라우팅 및 컴퍼넌트
- 2_1. 라우팅 설정하기
- 2_2. 컴퍼넌트 설정하기
- 2_3. SCSS 설정하기
- 3. 헤더 영역
- 3_1. 헤더 구조잡기
- 3_2. 헤더 디자인 설정
- 3_3. 헤더 데이터 작업
- 3_4. 헤더 토글 메뉴 작업하기
3. 헤더 영역
3.1 헤더 구조잡기
우선 기본 HTML구조를 리액트 구조에 맞추어서 작업을 해야 합니다.
<a href="#">은 <a href="/">로 변경해야 경고가 없어집니다.
또한 리액트에서는 class
를 사용하지 않습니다.
className
으로 변경해야 합니다.
import React from "react";
const Header = () => {
return (
<header id="header" role="banner">
<div className="header__inner">
<div className="header__logo">
<a href="/">portfolio<em>vite</em></a>
</div>
<nav className="header__nav" role="navigation" aria-label="메인 메뉴">
<ul>
<li><a href="#intro">intro</a></li>
<li><a href="#skill">skill</a></li>
<li><a href="#site">site</a></li>
<li><a href="#port">portfolio</a></li>
<li><a href="#contact">contact</a></li>
</ul>
</nav>
<div
className="header__nav__mobile"
id="headerToggle"
aria-controls="primary-menu"
aria-expanded="false"
role="button"
tabindex="0"
>
<span></span>
</div>
</div>
</header>
);
};
export default Header;
3.2 헤더 디자인 설정
기존에 CSS 방식보다는 어려울 수 있지만 사용하다 보면 더 편함을 느낄 수 있습니다. 반응형도 따로 작업하지 않고 해당 영역에 동시에 작업을 하였습니다.
#header {
@include position-fixed;
z-index: 10000;
}
.header__inner {
@include flex-between;
background-color: rgba(116, 99, 99, 0.1);
backdrop-filter: blur(15px);
padding: 1rem;
.header__logo {
font-size: 0.9rem;
text-align: center;
text-transform: uppercase;
line-height: 1;
em {
font-size: 10px;
display: block;
color: var(--black200);
}
}
.header__nav {
@media (max-width: 800px){
display: none;
&.show {
display: block;
ul {
display: block;
position: absolute;
right: 0;
top: 68px;
background-color: rgba(116,99,99,0.1);
backdrop-filter: blur(15px);
z-index: 10000;
min-width: 150px;
padding: 20px 0;
li {
display: block;
text-align: right;
a {
display: inline-block;
padding: 10px;
}
}
}
}
&.show + .header__nav__mobile span::before {
width: 20px;
}
&.show + .header__nav__mobile span::after {
width: 20px;
}
}
li {
display: inline;
a {
text-transform: uppercase;
font-size: 14px;
padding: 14px;
position: relative;
&::before {
content: '';
width: calc(100% - 28px);
height: 1px;
background-color: var(--black);
position: absolute;
left: 14px;
bottom: 10px;
transform: scaleX(0);
transition: all 0.3s;
}
&:hover::before {
transform: scaleX(1);
}
}
}
}
.header__nav__mobile {
display: none;
width: 40px;
height: 40px;
cursor: pointer;
@media (max-width: 800px){
display: block;
}
span {
display: block;
width: 40px;
height: 2px;
background-color: var(--black);
margin-top: 19px;
position: relative;
&::before {
content: "";
width: 40px;
height: 2px;
background-color: var(--black);
position: absolute;
right: 0;
top: 6px;
transition: width 0.3s;
}
&::after {
content: "";
width: 40px;
height: 2px;
background-color: var(--black);
position: absolute;
left: 0;
bottom: 6px;
transition: width 0.3s;
}
}
}
}
3.3 헤더 데이터 작업
리액트에서 데이터를 따로 작업하는 이유는 코드를 더 구조적이고 관리하기 쉽게 만들기 위해서입니다. 데이터를 따로 작업하여 관리함으로써 코드의 가독성과 유지보수성을 향상시킬 수 있습니다. 규모가 작은 사이트를 라면 불편할 수도 있는 작업이지만 규모가 커질 수록 관리하기가 쉬어집니다.
const headerNav = [
{
title: "intro",
url: "#intro",
},
{
title: "skill",
url: "#skill",
},
{
title: "site",
url: "#site",
},
{
title: "portfolio",
url: "#port",
},
{
title: "contact",
url: "#contact",
},
];
반복적으로 처리되는 부분을 map()
메서드를 통해 작업해보겠습니다.
map()은 JavaScript의 배열 메서드 중 하나로, 배열의 각 요소를 변환하여 새로운 배열을 반환하는 함수입니다.
React에서도 map()을 많이 활용하여 배열 데이터를 가지고 JSX를 생성하거나 처리하는 데 사용됩니다.
const Header = () => {
return (
<header id="header" role="banner">
<div className="header__inner">
<div className="header__logo">
<a href="/">portfolio<em>vite</em></a>
</div>
<nav className="header__nav" role="navigation" aria-label="메인 메뉴">
<ul>
{headerNav.map((nav, key) => (
<li key={key}>
<a href={nav.url}>{nav.title}</a>
</li>
))}
</ul>
</nav>
<div
className="header__nav__mobile"
id="headerToggle"
aria-controls="primary-menu"
aria-expanded="false"
role="button"
tabIndex="0"
>
<span></span>
</div>
</div>
</header>
);
};
잠깐 map() 메서드에 대해서 알아보고 가겠습니다.
currentValue
: 현재 배열 요소의 값, index
: 현재 배열 요소의 인덱스, array
: 원본 배열을 의미합니다.
const newArray = array.map((currentValue, index, array) => {
// 변환 로직을 작성하여 currentValue를 새로운 값으로 변환 후 반환
return newValue;
});
map()을 사용하면 원본 배열의 각 요소들을 순회하면서 새로운 배열을 만들 수 있습니다. 즉, 기존 배열을 변형하지 않고 새로운 배열을 생성합니다
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map((number) => {
return number * 2;
});
console.log(doubledNumbers); // [2, 4, 6, 8, 10]
배열 요소들을 JSX 엘리먼트로 변환하는 경우 (React에서 주로 사용하는 방법)
const names = ["Alice", "Bob", "Charlie"];
const nameList = names.map((name, index) => {
return <li key={index}>{name}</li>;
});
3.4 헤더 토글 메뉴 작업하기
반응형이 되었을 때 메뉴를 클릭하면 서브 메뉴가 나오도록 작업하겠습니다. 자바스크립트에서 썼던 방법을 써도 되지만, 효율성있고 리액트에 더 친화적인 방법을 쓰겠습니다.
import React, { useState } from "react";
const headerNav = [
{
title: "intro",
url: "#intro"
},
{
title: "skill",
url: "#skill"
},
{
title: "site",
url: "#site"
},
{
title: "portfolio",
url: "#port"
},
{
title: "contact",
url: "#contact"
}
];
const Header = () => {
const [show, setShow] = useState(false);
const toggleMenu = () => {
setShow((prevShow) => !prevShow);
};
return (
<header id="header" role="banner">
<div className="header__inner">
<div className="header__logo">
<a href="/">portfolio<em>react.js</em></a>
</div>
<nav className={`header__nav ${show ? "show" : ""}`} role="navigation" aria-label="메인 메뉴">
<ul>
{headerNav.map((nav, key) => (
<li key={key}>
<a href={nav.url}>{nav.title}</a>
</li>
))}
</ul>
</nav>
<div
className="header__nav__mobile"
id="headerToggle"
aria-controls="primary-menu"
aria-expanded={show ? "true" : "false"}
role="button"
tabIndex="0"
onClick={toggleMenu}
>
<span></span>
</div>
</div>
</header>
);
};
export default Header;
- const [show, setShow] = useState(false); : show라는 상태 변수를 생성하고 useState 훅으로 초기값을 false로 설정합니다. 이 변수는 모바일 메뉴의 표시 여부를 관리합니다. show가 true이면 모바일 메뉴가 나타나고, false이면 숨겨집니다.
- const toggleMenu = () => { ... }: toggleMenu라는 함수를 정의합니다. 이 함수는 모바일 메뉴를 표시하거나 숨기기 위해 show 상태를 변경하는 역할을 합니다. setShow 함수를 사용하여 show 상태를 업데이트합니다.
3. 마무리
이제 모든 코딩 준비가 되었습니다. 그럼 깃에 올립시다.
git add .
git commit -m "🥳 헤더영역 완료"
git push -u origin main
처음 배우는 개념들이 있으면 혼란스러울 수 있습니다. 하지만 몇 번 따라하면서 익히면 재미가 있습니다. 포기하지 말고 끝까지 잘 따라해주시면 감사하겠습니다. 그럼 이번 리액트도 끝까지 완성해봅시다.!
😘 이런 에러가 난다면...
리액트에서는 class
대신에 className
을 사용합니다.
class를 썼는지 확인해봐야 합니다.
react-dom.development.js:86 Warning: Invalid DOM property `class`. Did you mean `className`?
리액트에서는 tabindex
대신에 tabIndex
을 사용합니다.
tabindex를 썼는지 확인해봐야 합니다.
Warning: Invalid DOM property `tabindex`. Did you mean `tabIndex`?
예제 목록
- 1. 포트폴리오 사이트 만들기 : Vite-Site : 셋팅하기
- 2. 포트폴리오 사이트 만들기 : Vite-Site : 레이아웃 설정
- 3. 포트폴리오 사이트 만들기 : Vite-Site : 헤더 영역
- 4. 포트폴리오 사이트 만들기 : Vite-Site : 인트로 영역
- 5. 포트폴리오 사이트 만들기 : Vite-Site : 스킬 영역
- 6. 포트폴리오 사이트 만들기 : Vite-Site : 사이트 영역
- 7. 포트폴리오 사이트 만들기 : Vite-Site : 포트폴리오 영역
- 8. 포트폴리오 사이트 만들기 : Vite-Site : 연락처 영역
- 9. 포트폴리오 사이트 만들기 : Vite-Site : 푸터 영역
- 10. 포트폴리오 사이트 만들기 : Vite-Site : 마무리
- 11. 포트폴리오 사이트 만들기 : React-Site : 셋팅하기
- 12. 포트폴리오 사이트 만들기 : React-Site : 컨퍼넌트 설정
- 13. 포트폴리오 사이트 만들기 : React-Site : 헤더 영역
- 14. 포트폴리오 사이트 만들기 : React-Site : 인트로 영역
- 15. 포트폴리오 사이트 만들기 : React-Site : 스킬 영역
- 16. 포트폴리오 사이트 만들기 : React-Site : 사이트 영역
- 17. 포트폴리오 사이트 만들기 : React-Site : 포트폴리오 영역
- 18. 포트폴리오 사이트 만들기 : React-Site : 연락처 영역
- 19. 포트폴리오 사이트 만들기 : React-Site : 푸터 영역
- 20. 포트폴리오 사이트 만들기 : React-Site : 마무리
- 21. 포트폴리오 사이트 만들기 : Vue-Site : 셋팅하기
- 22. 포트폴리오 사이트 만들기 : Vue-Site : 컨퍼넌트 설정
- 23. 포트폴리오 사이트 만들기 : Vue-Site : 헤더 영역
- 24. 포트폴리오 사이트 만들기 : Vue-Site : 인트로 영역
- 25. 포트폴리오 사이트 만들기 : Vue-Site : 스킬 영역
- 26. 포트폴리오 사이트 만들기 : Vue-Site : 사이트 영역
- 27. 포트폴리오 사이트 만들기 : Vue-Site : 포트폴리오 영역
- 28. 포트폴리오 사이트 만들기 : Vue-Site : 연락처 영역
- 29. 포트폴리오 사이트 만들기 : Vue-Site : 푸터 영역
- 30. 포트폴리오 사이트 만들기 : Vue-Site : 마무리
- 31. 포트폴리오 사이트 만들기 : Next-Site : 셋팅하기
- 32. 포트폴리오 사이트 만들기 : Next-Site : 컨퍼넌트 설정
- 33. 포트폴리오 사이트 만들기 : Next-Site : 헤더 영역
- 34. 포트폴리오 사이트 만들기 : Next-Site : 인트로 영역
- 35. 포트폴리오 사이트 만들기 : Next-Site : 스킬 영역
- 36. 포트폴리오 사이트 만들기 : Next-Site : 사이트 영역
- 37. 포트폴리오 사이트 만들기 : Next-Site : 포트폴리오 영역
- 38. 포트폴리오 사이트 만들기 : Next-Site : 연락처 영역
- 39. 포트폴리오 사이트 만들기 : Next-Site : 푸터 영역
- 40. 포트폴리오 사이트 만들기 : Next-Site : 마무리
댓글