전에 자바스크립트로 대시보드 느낌의 태스크 매니저를 만들었었다. 원래는 포트폴리오를 준비하면서 리팩토링할 생각이었는데, 리액트를 배우던 어느 날 '어라, 이거 너무 리액트용 프로젝트 아니냐?' 하는 생각이 들었다.
멋모르던 시절에 그냥 html 페이지 여러 개로 만들다 보니 header나 side-bar처럼 중복되는 코드가 많았고 그 외에도 같은 기능을 하는 코드들이 많았다. 이걸 단순히 줄이는 것보다는 Component화 해서 SPA로 깔끔하게 새로 만드는 게 낫지 않을까 싶었고, 일단 한 번 해보려고 한다.
지난 번과 마찬가지로 아마 100퍼센트 다 구현하지는 못할 테지만, 아는 지식 모르는 지식 총동원해서 만들어 보자고. 이거 하나로 포트폴리오 끝내 보자고.
초기 폴더 구조는 이렇게 짰다. 공부할 겸 설명해 보자면
이 외에 필요한 파일이나 폴더는 차차 추가할 생각이다.
npm install react-router-dom@6
npm install --save styled-components
npm install react-icons --save
다른 라이브러리들은 필요할 때마다 추가할 예정이다.
라우팅(Routing)이란, 어떤 네트워크 내에서 데이터를 내보낼 경로를 행위 자체와 일련의 과정을 포함한 말이다. 여기에 우리가 아는 페이지의 개념을 덧붙인 페이지 라우팅(Page Routing)은 말 그대로 웹 서버가 요청에 알맞은 페이지를 선택하고 이를 사용자에게 반환하는 것을 말한다.
리액트는 SPA(Single Page Application)의 일종이기 때문에 React-router라는 라이브러리를 이용해서 라우팅함으로써 여러 페이지를 띄워주는 것처럼 보여줄 수 있다.
사용방법은 아주 쉽다. 먼저 React-router 라이브러리를 설치한 후, App.js 파일에 BrowserRouter와 Route, Routes를 import한다. 그리고 라우팅하고자 하는 요소를 <BrowserRouter>
로 감싼다.
<BrowserRouter>
내부에 페이자 역할을 할 요소들을 <Routes>
로 감싸고, 하나의 페이지 역할을 할 요소의 경로를 <Route path='url경로' element={컴포넌트}>
에 전달하면 끝.
페이지를 라우팅한 뒤에는 우선 모든 페이지에서 사용할 가장 기본 컴포넌트들부터 만들어 주었다. JS 프로젝트 때 헤더와 사이드바는 모든 페이지에 다 존재했었기 때문에 별다른 구상 없이 바로 만들 수 있었다.
JS 프로젝트와 달리 리액트로 만들면서 Styled-components 라이브러리로 CSS를 디자인했다. CSS 파일을 import해서 사용하면 CSS가 적용되는 범위가 제한되어 있지 않기 때문에 스타일이 전역에 적용되게 된다. 그러면 직접적으로 CSS 파일을 import하지 않은 다른 컴포넌트에도 스타일이 적용될 수 있기 때문에, 특정 스타일이 설정된 컴포넌트를 구축할 수 있도록 도와주는 라이브러리를 사용했다.
이렇게 하는 게 맞나?
먼저 가장 상위 div(StyledHeader)에만 적용했다가 코드가 너무 길어져서 나름대로 분리해 보았다.
header 태그에 flex와 space-between을 적용해서 내용물을 양 사이드 공백 없이 배치했다. div 대신 header를 사용한 것 외에 특별한 점은 없다.
header 태그 내부에서 input과 :focus가 중첩된 부분을 따로 분리했다. styled-components에서는 &자
를 사용하면 자기 자신을 선택할 수 있다.
사이드바를 열 수 있는 토글 버튼도 미디어 쿼리 사용을 위해 분리했다. 브라우저 너비가 768px보다 작을 때는 버튼을 보여주고 768px보다 클 때는 감추기 위해 display: none(block)
속성을 사용했다. visibility: hidden(visible)
속성과 다른 점은, 전자는 모습뿐만 아니라 요소가 차지하던 자리도 사라지는 것이고 후자는 요소가 자리는 그대로 차지하되 모습만 감추는 것이다. 나는 토글 버튼이 보이지 않을 때 그 자리를 비워두고 싶지 않았기 때문에 display 속성을 적용했다.
Dropdown은 이 글을 참고해서 만들었다. 이 전에는 부트스트랩에서 제공하는 드롭다운을 그대로 가져다 썼기 때문에 이번에는 CSS로 직접 구현하고 싶었다.
우선 일반 HTML 짜듯 레이아웃을 설정하고, 드롭다운 활성화 여부를 state에 저장했다. 클릭했을 때 리스트가 나와야 하므로 기본값은 false를 주었고, 이 state를 변경하는 함수(activeHandler)를 만들었다.
일전에 바닐라 JS로 토글을 구현할 때는 classList.toggle 메서드를 쓰면 그만이었지만, 이번에는 not 연산자를 사용해서 그 효과를 내주었다. 공부를 하면 할 수록 not 연산자가 쓰이는 곳이 많다는 걸 새삼 느낀다.
button을 클릭하면 activeHandler 함수를 따라 isActive가 true와 false를 넘나든다. 이때 state 값이 true이면 <Nav>
태그에 active가, false이면 inactive가 props로 전달되도록 삼항 연산자를 이용해서 조건을 부여했다.
styled-components를 이용해서 Nav에 미리 스타일을 설정해두었다. display: none
으로 감추어져 있던 nav에 active라는 props가 전달되면 display: block
으로 바뀌어서 nav 목록이 뿅 하고 나타난다.
nav 목록에는 useNavigate를 이용해서 링크를 걸어주었다.
이전 프로젝트와 마찬가지로 화면이 충분히 클 때는 왼쪽에 고정, 화면 사이즈가 작을 때는 사이드바 오픈 버튼만 남기고 닫혀 있게끔 만들어 주었다.
고정되어 있지 않을 때는 사용자가 필요에 따라 사이드바를 열고 닫을 수 있도록 열림 버튼과 닫힘 버튼을 만들었다.
우선 Dropdown과 마찬가지로 state를 만들어서 isVisible의 true/false 여부를 저장했고, 이를 sidebarToggler라는 함수를 만들어서 변경할 수 있게끔 했다. 그리고 이 함수를 (짤에서는 너무 빨리 지나가서 잘 안 보이는) 닫힘 버튼에 onClick 이벤트와 연결시켰다.
여기까지는 한 컴포넌트 내에서 이루어지는 동작이라 어려울 것이 없었지만, 문제는 열림 버튼이다. 닫혀 있는 사이드바를 열어야 하기 때문에 사이드바 아닌 헤더에 만들다 보니 이 둘을 연결시켜야 하기 때문이다. 바닐라 JS로 만들었을 때는 같은 페이지에 있는 요소라 DOM을 사용해서 쉽게 구현할 수 있었는데 이번에는 조금 복잡해서 잠시 미뤄두었다. 강의에서 배운 바에 의하면 둘의 공통 부모 컴포넌트인 App에 state를 끌어올렸다가 내려주는 방식으로 가능할 것 같은데, 우선은 다른 쉬운 것부터 해결하고 돌아와서 완성시켜야겠다.
한 번 만들어 봤던 걸 다시 만드는 작업이지만, 프레임워크가 달라지고 CSS 스타일도 달라져서 그런지 지루하지 않고 재미있다! 새로운 기술을 적용하느라 조금 생소하고 어렵기는 해도 확실히 눈에 바로바로 보이는 무언가를 만들 때가 가장 재미있는 것 같다.
아직 옛 버릇을 못 버려서 코드를 짜는 동시에 수정하려고 하느라 시간이 좀 오래 걸리는 것만 빼면 순조로운 것 같다.
일단 깃허브에 올려두고 열심히 만들고 열심히 다듬어 봐야겠다. JS 프로젝트에 비해 얼마나 발전된 결과물이 나올지 벌써부터 기대된다!