들어가며
08/25부터 3주간에 걸쳐 진행될
[Chapter 3] 주특기 주차가 시작되어
Lv.1 ~ 5 까지 페어 프로그래밍으로 과제가 진행되고 있습니다.
이번주는 Lv.3 페어과제로 모달과 버튼을 포함한 웹 페이지를 페어를 이루어
주어진 요구사항에 맞게 만들어 보는 과제를 화요일, 수요일(09/05, 09/06) 진행했습니다.
웹 페이지에서는 과제를 수행하기 위해
Redux-Toolkit, Styled-Components 의 props children, position(css) 의 개념을 이용했습니다.
과제 요구사항
- Modal 2개를 구현합니다.
- overlay 를 클릭했을 때 모달이 닫히는 형태와 그렇지 않은 형태
- Buuton 6개를 구현합니다.
- styled-components 를 이용해 props 를 적절하게 활용합니다.
- label 에 아이콘을 추가합니다.
- Input 2개를 구현합니다.
- 화페 단위, 콤마 , 가 찍히는 input
- Select 를 2개 구현합니다.
- select 를 클릭했을 때 부모 요소에 의해 가려지지 않는 select 와 그렇지 않은 형태
- 예시 : https://hh99-react-lv3.vercel.app/
React App
hh99-react-lv3.vercel.app
과제를 수행하며
Lv.3 모달 웹페이지를 구현하면서 className 을 일일이 수동으로 관리할 필요 없이 React 의 props 나 전역 속성을 기반으로 컴포넌트에 스타일 속성을 부여하기 때문에 Styled-components 가 간단하고 직관적인 강점을 갖고 있다는 점을 알게 되었습니다.
또한, 제가 생각하는 Redux-Toolkit 의 장점으로는
Redux 의 단점으로 꼽히는 보일러플레이트 코드가 줄어든다는 것입니다.
보일러플레이트 코드가 많으면, 코드의 예측가능성 측면에서 떨어지고, 코드해석이 어려워져, 휴먼에럴나 실수를 유발시킬 수 있습니다.
그리고, 패키지의 의존성을 줄여줍니다.
리덕스를 사용하다보면, redux devtool, immer, reselect 등 여러가지 라이브러리들을 설치하게 됩니다. 하지만, Redux-Toolkit 에는 많은 라이브러리들이 내장되어 있어 다른 라이브러리들의 의존성을 줄일 수 있다고 생각합니다.
https://github.com/yngjnhykk/react_Lv.3-css
GitHub - yngjnhykk/react_Lv.3-css
Contribute to yngjnhykk/react_Lv.3-css development by creating an account on GitHub.
github.com
https://react-lv-3-css.vercel.app/
React App
react-lv-3-css.vercel.app
react_Lv.3-css info
수행한 프로젝트의 기능들을 폴더별로 설명해보겠습니다.
- components
- LeftModal : 닫기 버튼과, 확인 버튼이 포함된 모달창으로, 모달의 외부영역을 클릭해도 모달창이 닫히지 않습니다.
- RightModal : 어떤 버튼 없이, 외부 영역을 클릭하면 닫히게 되는 모달창입니다.
- LeftSelect : select 의 option 창이 부모 요소로 보이는 박스 바깥으로 보이는 select 입니다.
- RightSelect : select 의 option 창이 부모 요소로 보이는 박스 바깥으로는 보이지 않는 select 입니다.
- modules
- reduxer.js : select 에서 클릭한 값을 initialState 로 설정하고, option 창에서 클릭한 값을 전역 변수로 관리하는 곳입니다.
- App.js : 메인 페이지입니다.
LeftModal
closeModal
닫기 버튼을 클릭했을 때, 모달을 닫는 기능을 하는 함수로 App.js 에서 false 를 초기값으로 useState 로 관리했던 leftModalOpen 변수를 LeftModal 컴포넌트의 props 로 받아와 모달을 켜기 위해 App.js 에서 true 로 바꾼 leftModalOpen 을 모달을 닫기 위해 다시 false 로 바꿉니다.
// 왼쪽 모달의 초기값 상태 관리
const [leftModalOpen, setLeftModalOpen] = useState(false);
// 모달을 띄우기 위해 상태를 true 로 바꾼 함수, showLeftModal
const showLeftModal = () => {
setLeftModalOpen(true);
};
// 모달 끄기
const closeModal = () => {
setLeftModalOpen(false);
};
RightModal
stopPropagation
모달의 외부 영역을 가리키는 div 태그에 LeftModal 컴포넌트의 닫기 버튼에 쓰인 방법과 같이 closeModal 함수를 onClick 으로 선언합니다. 하지만, 이렇게 되었을 때 외부 영역을 클릭하면, 모달이 닫히지만, 외부영역뿐 아니라 div 태그 안에 들어 있는 자식 태그의 모든 부분에 onClick = { closeModal } 이 적용됩니다. 이를 막기 위해 부모태그로의 이벤트 전파를 stop 중지하는 자바스크립트 내장 메서드인 stopPropagation 을 사용해 부모 컴포넌트인 div 태그에서 자식 태그인 모달의 div 태그로 이벤트가 전파되는 것을 방지 기능을 구현합니다.
<div className='wrap' onClick={closeModal}>
<div
className='container'
onClick={(e) => {
e.stopPropagation();
}}
>
<p>우측 모달창입니다.</p>
</div>
</div>
LeftSelect
onClickSelect
LeftSelect 는 왼쪽 select 의 option 창을 렌더링하는 컴포넌트입니다. 때문에 Option 을 선택했을때, 해당 태그의 value 를 innerText 를 통해 slected 로 변수선언하고, 이를 Redux-Toolkit 으로 선언한 전역 UPDATE1 리듀서 함수를 호출하기 위해 해당 액션타입을 반환하는 update_select1 을 import 해와 dispatch 로 전역함수를 호출합니다.
LeftSelect.jsx
import React from 'react';
import { styled } from 'styled-components';
import { useDispatch } from 'react-redux';
import { update_select1 } from '../redux/modules/reducer';
function LeftSelect({ showNHideLeftSelect }) {
const dispatch = useDispatch();
const OnClickSelect = (e) => {
const selected = e.target.innerText;
dispatch(update_select1(selected));
showNHideLeftSelect();
};
return (
<LeftSelectStyle>
<Option onClick={OnClickSelect}>리액트</Option>
<Option onClick={OnClickSelect}>자바</Option>
<Option onClick={OnClickSelect}>스프링</Option>
<Option onClick={OnClickSelect}>리액트 네이티브</Option>
<Option onClick={OnClickSelect}>타입스크립트</Option>
<Option onClick={OnClickSelect}>넥스트 제이에스</Option>
<Option onClick={OnClickSelect}>노드 제이에스</Option>
</LeftSelectStyle>
);
}
reducer.js
import { createSlice } from '@reduxjs/toolkit';
export const update_select1 = (selected) => {
return {
type: UPDATE1,
selected: selected,
};
};
export const update_select2 = (selected) => {
return {
type: UPDATE2,
selected: selected,
};
};
const initialState = {
select1: '리액트',
select2: '리액트',
};
const counterSlice = createSlice({
name: 'reducer',
initialState,
reducers: {
UPDATE1: (state, action) => {
state.select1 = action.selected;
},
UPDATE2: (state, action) => {
state.select2 = action.selected;
},
},
});
export const { UPDATE1, UPDATE2 } = counterSlice.actions;
export default counterSlice.reducer;
하지만, 여기서 끝이 아닙니다. 중요한 기능이 한 가지 남았는데요. select 의 option 창이 부모태그의 영역으로 보이는 박스의 바깥 부분에서도 보이게 css 코드로 구현해야 합니다. 방법은 의외로 간단했는데요. position 속성을 이용해 LeftSelect 컴포넌트의 부모 태그를 body 로 하게끔 해서 left 와 top 으로 위치를 맞춰 조정해주었습니다. 이를 위해 LeftSelect 컴포넌트는 가장 바깥에서 태그를 붙여주었습니다.
function App() {
(...)
return (
<div>
<Button>
(...)
</Button>
<h1>Input</h1>
<Input>
(...)
</Input>
<Modal>
(...)
</Modal>
<Select>
(...)
</Select>
{leftSelectOpen && (
<LeftSelect showNHideLeftSelect={showNHideLeftSelect} />
)}
</div>
);
RightSelect
RightSelect 도 LeftSelect 와 기능적인 면으로는 마찬가지입니다. 하지만, overflow: hidden 이라는 css 코드를 이용하기 위해 position 속성을 이용해 부모태그를 Select 태그로 갖기 위해 Select 태그 안에서 선언했습니다.
function App() {
(...)
return (
<div>
<Button>
(...)
</Button>
<h1>Input</h1>
<Input>
(...)
</Input>
<Modal>
(...)
</Modal>
<Select>
(...)
{rightSelectOpen && (
<RightSelect showNHideRightSelect={showNHideRightSelect} />
)}
</Select>
{leftSelectOpen && (
<LeftSelect showNHideLeftSelect={showNHideLeftSelect} />
)}
</div>
);
'항해 16기 > Week I Learned' 카테고리의 다른 글
[항해 70일차] WIL_translate 를 이용한 Carousel(캐러셀) 구현 (0) | 2023.10.25 |
---|---|
[항해 63일차] WIL_React-Query 에 대해 온전한 이해 (0) | 2023.10.15 |
[항해 21일차] WIL_Lv.2 redux_todolist (0) | 2023.09.03 |
[항해 7일차] WIL_숫자야구게임 (0) | 2023.08.21 |
[항해 -1일차] WIL_1 사전 스터디(css 기초), mini 프로젝트 (0) | 2023.08.13 |