12. Styled-components : Dialog 만들기 - 컴포넌트 스타일링 | 벨로퍼트
이번에는 기존 화면을 가리게 되면서 정보를 보여주게 되는 Dialog 컴포넌트를 만들어보겠습니다.
이 컴포넌트를 만드는 과정에서 우리가 아까 만들었던 Button 컴포넌트를 재사용하게 됩니다.
트랜지션 효과는 나중에 구현을 해주겠습니다.
우선 components 디렉터리에 Dialog.js 파일을 생성 후 다음 코드를 입력해보세요.
/src/components/Dialog.js :
import React from 'react';
import styled from 'styled-components';
import Button from './Button';
const DarkBackground = styled.div`
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.8);
`;
const DialogBlock = styled.div`
width: 320px;
padding: 1.5rem;
background: white;
border-radius: 2px;
h3 {
margin: 0;
font-size: 1.5rem;
}
p {
font-size: 1.125rem;
}
`;
const ButtonGroup = styled.div`
margin-top: 3rem;
display: flex;
justify-content: flex-end;
`;
function Dialog({ title, children, confirmText, cancelText }) {
return (
<DarkBackground>
<DialogBlock>
<h3>{title}</h3>
<p>{children}</p>
<ButtonGroup>
<Button color="gray">{cancelText}</Button>
<Button color="pink">{confirmText}</Button>
</ButtonGroup>
</DialogBlock>
</DarkBackground>
);
}
Dialog.defaultProps = {
confirmText: '확인',
cancelText: '취소'
};
export default Dialog;
우리가 h3, 과 p 를 스타일링 때 굳이 다음과 같이 따로 따로 컴포넌트를 만들어주지 않아도
const Title = styled.h3``;
const Description = styled.p``;
styled-components 에서도 Nested CSS 문법을 사용 할 수 있기 때문에 DialogBlock 안에 있는 h3 와 p 에게 특정 스타일을 주고 싶다면 다음과 같이 작성 할 수 있답니다.
const DialogBlock = styled.div`
h3 { ... }
p { ... }
`;
자, 이제 이 컴포넌트를 App 에 렌더링해보세요.
/src/App.js :
// ...
import Dialog from './components/Dialog';
// ...
function App() {
return (
<ThemeProvider
theme={{
palette: {
blue: '#228be6',
gray: '#495057',
pink: '#f06595'
}
}}
>
<>
<AppBlock>
// ...
</AppBlock>
<Dialog
title="정말로 삭제하시겠습니까?"
confirmText="삭제"
cancelText="취소"
>
데이터를 정말로 삭제하시겠습니까?
</Dialog>
</>
</ThemeProvider>
);
}
// ...
Dialog 컴포넌트를 예시 내용과 함께 AppBlock 하단에 넣어주었으며, ThemeProvider 내부는 하나의 리액트 엘리먼트로 감싸져있어야 하기 때문에 AppBlock 과 Dialog 를 <></> 으로 감싸주었습니다.
지금 보면 이 Dialog 에서는 취소 버튼과 삭제 버튼의 간격이 조금 넓어보이는 느낌이 있는데요, 만약에 styled-components로 컴포넌트의 스타일을 특정 상황에서 덮어쓰는 방법에 대해서 알아보겠습니다.
Dialog.js 에서 다음과 같이 ShortMarginButton 을 만들고 기존 Button 을 대체시켜보세요.
/src/components/Dialog.js :
//...
const ShortMarginButton = styled(Button)`
& + & {
margin-left: 0.5rem;
}
`;
function Dialog({ title, children, confirmText, cancelText }) {
return (
<DarkBackground>
<DialogBlock>
<h3>{title}</h3>
<p>{children}</p>
<ButtonGroup>
<ShortMarginButton color="gray">{cancelText}</ShortMarginButton>
<ShortMarginButton color="pink">{confirmText}</ShortMarginButton>
</ButtonGroup>
</DialogBlock>
</DarkBackground>
);
}
// ...
여백이 줄어들었나요? 이렇게 컴포넌트의 스타일을 커스터마이징 할 때에는 해당 컴포넌트에서 className props 를 내부 엘리먼트에게 전달이 되고 있는지 확인해주어야 합니다.
const MyComponent = ({ className }) => {
return <div className={className}></div>
};
const ExtendedComponent = styled(MyComponent)`
background: black;
`;
참고로 우리가 만든 Button 컴포넌트의 경우에는 ...rest 를 통하여 전달이 되고 있습니다.
컴포넌트의 모양새를 모두 갖추었으면 열고 닫을 수 있는 기능을 구현해봅시다. Dialog 에서 onConfirm 과 onCancel 을 props 로 받아오도록 하고 해당 함수들을 각 버튼들에게 onClick 으로 설정해주세요.
그리고, visible props 도 받아와서 이 값이 false 일 때 컴포넌트에서 null 을 반환하도록 설정해주세요.
/src/components/Dialog.js :
// ...
function Dialog({
title,
children,
confirmText,
cancelText,
onConfirm,
onCancel,
visible
}) {
if (!visible) return null;
return (
// ...
);
}
// ...
그 다음에는, App 컴포넌트에서 useState 를 사용하여 Dialog 를 가시성 상태를 관리해보세요.
/src/App.js :
// ...
function App() {
const [dialog, setDialog] = useState(false);
const onClick = () => {
setDialog(true);
};
const onConfirm = () => {
console.log('확인');
setDialog(false);
};
const onCancel = () => {
console.log('취소');
setDialog(false);
};
return (
<ThemeProvider
theme={{
palette: {
blue: '#228be6',
gray: '#495057',
pink: '#f06595'
}
}}
>
<>
<AppBlock>
<Button size="large" color="pink" fullWidth onClick={onClick}>
삭제
</Button>
</AppBlock>
<Dialog
title="정말로 삭제하시겠습니까?"
confirmText="삭제"
cancelText="취소"
onConfirm={onConfirm}
onCancel={onCancel}
visible={dialog}
>
데이터를 정말로 삭제하시겠습니까?
</Dialog>
</>
</ThemeProvider>
);
}
// ...
맨 아래에 있는 큰 핑크색 버튼의 이름을 "삭제" 로 변경 후 해당 버튼을 누르면 우리가 만든 Dialog 가 보여지도록 설정을 했고,
Dialog 에 onConfirm, onCancel, visible 값을 전달해주었습니다.
브라우저에서 삭제 버튼을 눌러서 Dialog 가 잘 작동하는지 확인해보세요.
버튼 눌렀을 때 콘솔에 확인/취소 텍스트가 출력되는지도 확인하세요.