처음에는 넷이서 위의 링크의 페이지를 똑같이 만들기로 했다.
하지만 한명이 나가게 됐고 세명에서 만들게 되었따.
7월10일 저녁에 구글밋으로 회의를 해서 banner를 만들기로 했다.
제일 처음 작업할건 저곳에 들어가는 최소한의 공통 컴포넌트의 개발이다.
종류는 Button
, Name
, ColorPicker
, InputNumber
이 있다.
여기서 나는 ColorPicker와 전체적인 레이아웃을 담당했다.
레이아웃은 Header, Content, Banner, Controls를 나눠서 개발했으며
ColorPicker는 React-Color 라이브러리를 사용해 간단하게 개발했다.
전에는 컴포넌트를 개발하면 그 안에 mvc를 모두 때려박았었다.
그리고 이렇게 개발한 컴포넌트들은 모두 components
폴더에 때려 박았었다.
근데 이렇게 하니 나중에 어떤 컴포넌트가 어디서 어떻게 작동하는지 파악하는데 시간이 걸렸고,
컴포넌트를 찾아도 주석만 읽고 과거의 내가 어떻게 코드를 짰는지 파악하는데도 시간이 걸렸다.
그래서 이번에는 조금 다르게 접근했다.
컴포넌트 이름은 폴더로 구분하고 그 안에 index.tsx
, style.ts
를 생성했다.
스타일과 비즈니스 로직 부분을 나눠주니 코드가 훨씬 읽기 좋아졌다.
이런식으로 말이다.
components
├── Content
│ ├── Banner
│ │ ├── index.tsx
│ │ └── style.ts
│ ├── Controls
│ │ ├── ColorPicker
│ │ │ ├── index.tsx
│ │ │ └── style.ts
│ │ ├── index.tsx
│ │ └── style.ts
│ ├── index.tsx
│ └── style.ts
└── Header
├── index.tsx
└── style.ts
폴더로 컴포넌트를 구분해줬기 떄문에 더 명확해졌다.
하지만 이 방법의 단점은 아직 모르겠다.
다른 프로젝트에도 적용해봐야 어떤 단점이 있는지 알것같다.
그렇게 해서 아래와 같은 결과가 나왔다.
원래 페이지는 이렇다.
두개를 비교해보면 상당히 비슷하게 작성했다.
코드는 여기에서 볼 수 있다.
color picker
는 아래와 같은 기능을 한다.
위의 기능을 아래의 코드로 구현해봤다.
// ColorPicker/index.tsx
import React, { useRef, useState } from 'react';
import ColorPickerBlock, { Picker, PickerCircle } from './style'
import {SketchPicker, ColorResult} from 'react-color';
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
import { changeBackgroundColor, changeBorderColor, changeForegroundColor } from '../../../../store/optionSlice';
import useOutsideDetect from '../../../../hooks/useOutsideClick';
interface ColorPickerProps {
whosColorChange: 'backgroundColor' | 'foregroundColor' | 'borderColor';
changeColorFunction:
| typeof changeBackgroundColor
| typeof changeForegroundColor
| typeof changeBorderColor;
}
const ColorPicker = ({
whosColorChange,
changeColorFunction
}:ColorPickerProps) => {
const color = useAppSelector(state=>state.option[whosColorChange]);
const dispatch = useAppDispatch();
const [isClick, setIsClick] = useState(false);
const circleRef = useRef<HTMLDivElement>(null);
useOutsideDetect(circleRef, () => {
setIsClick(false);
});
const handleChangePicker = (color: ColorResult) => {
dispatch(changeColorFunction(color.hex));
};
const handleClickPickerCircle = () => {
setIsClick(!isClick);
}
return (
<ColorPickerBlock>
<PickerCircle
color={color}
onClick={handleClickPickerCircle}
ref={circleRef}
>
{isClick &&
<Picker>
<SketchPicker
color={color}
onChange={handleChangePicker}
/>
</Picker>
}
</PickerCircle>
</ColorPickerBlock>
);
}
export default ColorPicker;
// ColorPicker/style.ts
import styled, { css } from "styled-components";
import {darken} from 'polished'
interface TPickerCircle {
color: string;
}
export default styled.div`
width: 100%;
height: 100%;
position: relative;
z-index: 100;
`;
export const PickerCircle = styled.div<TPickerCircle>`
width: 40px;
height: 40px;
border-radius: 50%;
${({color}) => css`
background: ${color};
border: 3px solid ${darken(0.2, color)};
`};
cursor: pointer;
`;
export const Picker = styled.div`
position: absolute;
left: -220px;
top: 20px;
`;
코드를 살펴보면 우선 props
로 whosColorChange
, changeColorFunction
를 받는다.
whosColorChange
는 store에서 어떤 대상의 색상을 바꿀지 선택하기위해 받아왔다.
그래서 이런식으로 값을 받아오고
const color = useAppSelector(state=>state.option[whosColorChange]);
dispatch는 이런식으로 해줬다.
// 컬러피커의 색상을 골랐을 때 호출되는 함수.
const handleChangePicker = (color: ColorResult) => {
dispatch(changeColorFunction(color.hex));
};
위의 코드를 보면 useSelector
, useDispatch
가 아니라
useAppSelector
, useAppDispath
를 쓴걸 볼 수 있다.
이렇게 쓴 이유는 사전에 모든 타입에 대해 선언해두고 편하게 쓰기 위해서 이렇게 했다.
// reduxStore.ts
import { configureStore } from '@reduxjs/toolkit';
import optionReducer from './optionSlice'
export const store = configureStore({
reducer: {
option: optionReducer,
},
// middleware: [thunk],
devTools: true
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// hooks.ts
import { useSelector } from "react-redux";
import { TypedUseSelectorHook, useDispatch } from "react-redux";
import { AppDispatch, RootState } from "./reduxStore";
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
요롷게 말이다.
useEffect()
를 사용해 랜덤한 값을 넣어준후
ColorPicker에 props를 넣어주기만 하면 간단하게 사용할 수 있다.
import React, { useEffect } from 'react';
import styled from 'styled-components';
import ColorPicker from './components/Content/Controls/ColorPicker';
import { useAppDispatch } from './store/hooks';
import { changeBackgroundColor } from './store/optionSlice';
import { getRandomColor } from './utils/colorUtil';
const App = () => {
const dispatch = useAppDispatch();
useEffect(()=>{
const randomColor = getRandomColor();
dispatch(changeBackgroundColor(randomColor));
},[])
return (
<Test>
<ColorPicker
whosColorChange={'backgroundColor'}
changeColorFunction={changeBackgroundColor}
/>
</Test>
);
};
export default App;
const Test = styled.div``;
결과는 이렇다.
이제 이렇게 만든 컴포넌트들을 조합해 더 큰 컴포넌트를 만들 일만 남았다.
오늘 저녁에 어떻게 만들지 회의 후 다시 만들게 될것같다.
끗.