리액트에서 컴포넌트를 참조하거나 변수를 만들고자 할 때 사용
HTML 에서는 DOM 을 선택하고자 할 때는 id를 부여한 후 getElementByid 함수를 이용하거나 querySelector 라는 함수에 선택자를 대입해서 선택
HTML 에서 입력된 값이나 모양을 선택하기 위해 DOM 객체를 찾아오지만 리액트에서는 입력된 값은 state를 이용해서 사용하기 때문에 DOM 객체를 선택할 사항이 그리 많지 않다
ref 사용
css 파일 생성 - src/RefComponent.css
클래스 컴포넌트 생성 - src/RefComponent.jsx
Component.css
.success{
background-color: lightgreen;
}
.failure{
background-color: lightcoral;
}
RefComponent.jsx
import React, {Component} from 'react'
import './RefComponent.css'
class RefComponent extends Component{
//다른 객체를 잠조할 수 있는 변수를 생성
input = React.createRef();
//state : 변경 가능한 데이터, state의 값이 변경되면 Component는 자동으로
//rerendering (재출력)
//클래스 형 컴포넌트에서는 생성자(this.state로 생성)나 함수 외부에서 생성이 가능하다
//자바와 같은 객체 지향 언어에서는 메서드 내에서 변수를 호출하면
//메서드 내에서 찾고 없으면 클래스 그래도 없으면 상위 클래스로 찾아간다
//파이썬 혹은 자바스크립에서는 함수 내에서 변수를 사용했는데 없으면 생성한다
//파이썬 이나 자바스크립트에서는 인스턴스가 소유하는 변수를 만들 때나 사용할 때
//this 를 붙여야 한다
state = {
password:'',
clicked: false,
validated : false
}
//이번트 함수
handleChange = (e) => {
// 이벤트가 발생한 객체에 설정된 값을 password라는 state에 대입
this.setState({
password:e.target.value
})
}
handleClick = (e) => {
this.setState({
clicked:true,
validated: this.state.password === '0000'
})
/*let input = document.getElementById('input');
input.focus();*/
//input 이라는 ref 가 참조하는 객체에 포커스를 설정
this.input.current.focus();
}
render(){
return(
<>
<input
id = 'input'
ref = {this.input}
type='password'
onChange = {this.handleChagne}
value = {this.state.password}
className = {this.state.clicked ? this.state.validated ? 'success' : 'failure':''}/>
<button onClick={this.handleClick}>검증</button>
</>
)
}
}
export default RefComponent;
App.js
import './App.css';
import RefComponent from './RefComponent';
function App() {
return (
<>
<RefComponent/>
</>
);
}
export default App;
RefComponent.jsx
import React, {Component} from 'react'
import './RefComponent.css'
class RefComponent extends Component{
constructor(props){
super(props);
let ar = [10,20,30,40];
//배열을 순회하면서 1을 더해보자
let result = ar.map((element, index, br) => {return element + 1});
console.log(result);
}
//다른 객체를 잠조할 수 있는 변수를 생성
input = React.createRef();
//state : 변경 가능한 데이터, state의 값이 변경되면 Component는 자동으로
//rerendering (재출력)
//클래스 형 컴포넌트에서는 생성자(this.state로 생성)나 함수 외부에서 생성이 가능하다
//자바와 같은 객체 지향 언어에서는 메서드 내에서 변수를 호출하면
//메서드 내에서 찾고 없으면 클래스 그래도 없으면 상위 클래스로 찾아간다
//파이썬 혹은 자바스크립에서는 함수 내에서 변수를 사용했는데 없으면 생성한다
//파이썬 이나 자바스크립트에서는 인스턴스가 소유하는 변수를 만들 때나 사용할 때
//this 를 붙여야 한다
state = {
password:'',
clicked: false,
validated : false
}
//이번트 함수
handleChange = (e) => {
// 이벤트가 발생한 객체에 설정된 값을 password라는 state에 대입
this.setState({
password:e.target.value
})
}
handleClick = (e) => {
this.setState({
clicked:true,
validated: this.state.password === '0000'
})
/*let input = document.getElementById('input');
input.focus();*/
//input 이라는 ref 가 참조하는 객체에 포커스를 설정
this.input.current.focus();
}
render(){
return(
<>
<input
id = 'input'
ref = {this.input}
type='password'
onChange = {this.handleChange}
value = {this.state.password}
className = {this.state.clicked ? this.state.validated ? 'success' : 'failure':''}/>
<button onClick={this.handleClick}>검증</button>
</>
)
}
}
export default RefComponent;
RefComponent.jsx
let ar = ['aaa','bbb','ccc','ddd'];
//배열을 순회하면서 1을 더해보자
let result = ar.map((element, index, br) => {return element.substring(0,2) + '...'});
console.log(result);
RefComponent.jsx
// true 를 리턴한 데이터만 골라서 배열을 생성
result = ar.filter((element, index, br)=> {
return element.length > 2
});
console.log(result);
RefComponent.jsx
ar = [200, 100, 400, 40];
//자바스크립트의 배열을 정렬하면 모두 문자열로 변환해서 크기 비교
//숫자의 오름차순 정렬을 하고자 하는 경우는 2개의 매개변수를 가지고
//정수를 리턴하는 함수를 대입해야한다
//잎의 데이터가 크면 양수 같으면 0 작으면 음수를 리턴하면 된다.
ar.sort((a,b) => {return a-b;});
console.log(ar)
LiterationComponent.jsx
const LiterationComponent = () => {
const names = ['ab','cd','ef','gh'];
const namesList = names.map(
(name,index)=>{return <li key = {index}> {name}</li>})
return(
<ul>
{namesList}
</ul>
)
}
export default LiterationComponent;
App.js
import './App.css';
import RefComponent from './RefComponent';
import LiterationComponent from './LiterationComponent';
function App() {
return (
<>
<LiterationComponent/>
<RefComponent/>
</>
);
}
export default App;
데이터 추가
데이터 삭제
배열의 데이터를 삭제할 때 delete 사용이 가능한데 delete는 원본 배열에서 요소를 삭제한다
slice 와 concat의 조합 또는 filter를 이용하여 특정 데이터를 제거한 배열을 생성하는 것이 가능
LiterationComponent.jsx
import React,{Component} from "react";
class LiterationComponent extends Component {
state = {
names: ['나','는','낭','만','고','양'],
name:''
}
handleChange = (e) => {
this.setState({
[e.target.name]:e.target.value
})
}
handleClick = (e) => {
this.setState({
names:this.state.names.concat(this.state.name),
name:''
})
}
render(){
const nameList = this.state.names.map(
(name,index) => {return <li key={index}>{name}</li>});
return (
<>
<input value = {this.state.name} onChange = {this.handleChange}
name = 'name'/>
<button onClick={this.handleClick}>추가</button>
{nameList}
</>
)
}
}
export default LiterationComponent;
LiterationComponent.jsx
import React,{Component} from "react";
class LiterationComponent extends Component {
state = {
names: ['나','는','낭','만','고','양'],
name:''
}
handleChange = (e) => {
this.setState({
[e.target.name]:e.target.value
})
}
handleClick = (e) => {
this.setState({
names:this.state.names.concat(this.state.name),
name:''
})
}
handleRemove = (e) => {
alert('더블 클릭')
}
render(){
const nameList = this.state.names.map(
(name,index) => {return <li key={index}
onDoubleClick={this.handleRemove}>{name}</li>});
return (
<>
<input value = {this.state.name} onChange = {this.handleChange}
name = 'name'/>
<button onClick={this.handleClick}>추가</button>
{nameList}
</>
)
}
}
export default LiterationComponent;
import React,{Component} from "react";
class LiterationComponent extends Component {
state = {
names: ['나','는','낭','만','고','양'],
name:''
}
handleChange = (e) => {
this.setState({
[e.target.name]:e.target.value
})
}
handleClick = (e) => {
this.setState({
names:this.state.names.concat(this.state.name),
name:''
})
}
//반복문으로 생성한 컴포넌트에 이벤트를 연결할 때는
//몇 번째 데이터에게 발생한 것인지 알 수 있도록 index 혹은 id를 넘겨줘야함
handleRemove = (index) => {
alert(index)
}
render(){
const nameList = this.state.names.map(
(name,index) => {return <li key={index}
onDoubleClick={() => this.handleRemove(index)}>{name}</li>});
return (
<>
<input value = {this.state.name} onChange = {this.handleChange}
name = 'name'/>
<button onClick={this.handleClick}>추가</button>
{nameList}
</>
)
}
}
export default LiterationComponent;
LiterationComponent.jsx
import React,{Component} from "react";
class LiterationComponent extends Component {
state = {
names: ['나','는','낭','만','고','양'],
name:''
}
handleChange = (e) => {
this.setState({
[e.target.name]:e.target.value
})
}
handleClick = (e) => {
this.setState({
names:this.state.names.concat(this.state.name),
name:''
})
}
//반복문으로 생성한 컴포넌트에 이벤트를 연결할 때는
//몇 번째 데이터에게 발생한 것인지 알 수 있도록 index 혹은 id를 넘겨줘야함
//구조 분해 할당 이용해서 코드 정리
handleRemove = (index) => {
const {names} = this.state;
this.setState({
names:names.slice(0,index).concat(
names.slice(index +1 , names.length)
)
})
}
render(){
const nameList = this.state.names.map(
(name,index) => {return <li key={index}
onDoubleClick={() => this.handleRemove(index)}>{name}</li>});
return (
<>
<input value = {this.state.name} onChange = {this.handleChange}
name = 'name'/>
<button onClick={this.handleClick}>추가</button>
{nameList}
</>
)
}
}
export default LiterationComponent;
LiterationComponent.jsx
this.state.names.slice
----
const {names} = this.state;
선언 후
names.slice 로 수정
지역 변수의 데이터를 다른 곳에서 사용하기 위함
코드를 간결하게 하거나 접근을 빠르게 하기 위함
[인덱스] 또는 .이름을 이용해서 접근을 하게 되면 기준점을 찾아간 후 데이터를 찾아감
자주 사용하는 데이터는 인덱스 혹은 .이름이 아닌 직접 접근하도록 하면 코드도 간결해지고 접근 속도도 빨라지는 장점이 있음
LiterationComponent.jsx
import React,{Component} from "react";
class LiterationComponent extends Component {
state = {
names: ['나','는','낭','만','고','양'],
name:''
}
handleChange = (e) => {
this.setState({
[e.target.name]:e.target.value
})
}
handleClick = (e) => {
this.setState({
names:this.state.names.concat(this.state.name),
name:''
})
}
//반복문으로 생성한 컴포넌트에 이벤트를 연결할 때는
//몇 번째 데이터에게 발생한 것인지 알 수 있도록 index 혹은 id를 넘겨줘야함
handleRemove = (index) => {
//slice 함수를 이용해서 선택한 항목 앞 까지 와 선택한 항목 뒤의 내용을 합치기
const {names} = this.state;
/*
this.setState({
names:names.slice(0,index).concat(
names.slice(index +1 , names.length)
)
})*/
this.setState({
names: names.filter((element, i ,ar) => {
return i !== index;
})
})
}
render(){
const nameList = this.state.names.map(
(name,index) => {return <li key={index}
onDoubleClick={() => this.handleRemove(index)}>{name}</li>});
return (
<>
<input value = {this.state.name} onChange = {this.handleChange}
name = 'name'/>
<button onClick={this.handleClick}>추가</button>
{nameList}
</>
)
}
}
export default LiterationComponent;
☑️ iter
iterator(반복자)
배열[인덱스] 형태로 접근하는 방식
배열은 배열의 시작 위치를 참조
배열[3] -> 배열의 시작 위치를 찾고 그 다음에 3칸을 건너뛰어 데이터를 찾는 방식
이터레이션을 이용한 접근
Mount (컴포넌트가 화면에 출력) -> Update(props 나 state 의 변경 등으로 리랜더링) -> Unmount(소멸)
Mount 될 때 호출되는 메서드
state의 초기화 설정을 여기서 수행
constructor : 생성자, 클래스가 인스턴스를 만들 때 호출되는 메서드
getDeriveedStateFromProps : props에 있는 값을 state에 동기화 시키는 메서드
render : UI 를 랜더링 하는 메서드
필수 구현 메서드
모양을 정의
컴포넌트 내부의 구성 요소를 가져오거나 수정하는 작업은 안됨
componentDidMount : 웹 브라우저에 컴포넌트가 출력된 후 호출되는 메서드
Update 될 때 호출되는 메서드
getDerivedStateFromProps : 상위 컴포넌트가 리랜더링 되거나 props 에 변화가 생기면 호출
shouldComponentUpdate : state 가 변경되면 호출되는 메서드로 이 메서드에서 false를 리턴하면 리랜더링을 하지 않음
render : forceUpdate 라는 함수를 호출해서 강제로 reder를 호출할 수 있음 (트리거)
getSnapshotBeforeUpdate : 이전 내용을 수정하기 전에 호출
componentDidUpdate : 컴포넌트가 다시 출력되고 난 후 호출되는 메서드
Unmount 될 때 호출되는 메서드
LiterationComponent.jsx
import React,{Component} from "react";
class LiterationComponent extends Component {
constructor(props){
super(props);
console.log('생성자');
}
componentDidMount(){
console.log('컴포넌트가 화면에 출력된 후 호출되는 메서드');
}
componentDidUpdate(prevProps,prevState,snapshot){
console.log('컴포넌트가 재 출력된 후 호출되는 메서드');
console.log('props:' + prevProps);
console.log('state:' + prevState);
console.log('과거의 값:' + snapshot);
}
state = {
names: ['나','는','낭','만','고','양'],
name:''
}
handleChange = (e) => {
this.setState({
[e.target.name]:e.target.value
})
}
handleClick = (e) => {
this.setState({
names:this.state.names.concat(this.state.name),
name:''
})
}
//반복문으로 생성한 컴포넌트에 이벤트를 연결할 때는
//몇 번째 데이터에게 발생한 것인지 알 수 있도록 index 혹은 id를 넘겨줘야함
handleRemove = (index) => {
//slice 함수를 이영해서 선택한 항목 앞 까지 와 선택한 항목영역
const {names} = this.state;
/*
this.setState({
names:names.slice(0,index).concat(
names.slice(index +1 , names.length)
)
})*/
this.setState({
names: names.filter((element, i ,ar) => {
return i !== index;
})
})
}
render(){
const nameList = this.state.names.map(
(name,index) => {return <li key={index}
onDoubleClick={() => this.handleRemove(index)}>{name}</li>});
return (
<>
<input value = {this.state.name} onChange = {this.handleChange}
name = 'name'/>
<button onClick={this.handleClick}>추가</button>
{nameList}
</>
)
}
}
export default LiterationComponent;
개발 모드에서는 컴포넌트가 2번씩 랜더링 - StrictMode로 수행되기 때문
컴포넌트를 1번만 랜더링 하는 방법
index.js 에서 strictMode를 제거
운영 모드로 실행
npm run build - 빌드
npm install -g serve - 웹서버를 설치
serve -s build
import React from "react";
//별도의 css 파일을 만들지 않고 내부에 스타일을 설정할 때는 주의 해야함
//css의 속성이 camel case로 이름이 변경된다.
// - 기호도 없어짐
//예) background-color : backgroundColor 로 바뀜
// 스타일을 객체 형태로 생성해서 설정
const styles = {
wrapper:{
margin:8,
padding:8,
display:'flex',
felxDirection:'row',
border:'1px solid gray',
borderRadius: 16
},
imageContainer:{},
image:{
width:50,
height:50,
borderRadius:25
},
contentContainer:{
marginLeft:8,
display:'flex',
felxDirection:'column',
justifyContent: 'center'
},
nameText:{
color:'black',
fontSize:16,
fontWeight:'bold'
},
commentText:{
color:'black',
fontSize: 16
}
}
function Comment(props){
return(
<>
<div style={styles.wrapper}>
<div style={styles.imageContainer}>
<img src =
'https://upload.wikimedia.org/wikipedia/commons/8/89/Portrait_Placeholder.png'
style={styles.image}
alt='이미지를 다운로드 할 수 없당'/>
</div>
</div>
<div style={styles.contentContainer}>
<span style={styles.nameText}>{props.name}</span>
<span style={styles.commentText}>{props.comment}</span>
</div>
</>
)
}
export default Comment;
CommentList.jsx
import React from "react";
import Comment from "./Comment";
//출력할 데이터 생성
const comments = [
{
name: 'lee',
comment: 'hello'
},
{
name: 'eui',
comment: 'bonjur'
},
{
name: 'joo',
comment: 'nihao'
},
]
function CommentList(props){
return(
<>
{
comments.map((comment,index,ar) => {
return (<Comment name={comment.name}
comment={comment.comment}/>);
})
}
</>
)
}
export default CommentList;
App.js
import './App.css';
import RefComponent from './RefComponent';
import LiterationComponent from './LiterationComponent';
import CommentList from './CommentList';
function App() {
return (
<>
<LiterationComponent/>
<RefComponent/>
<CommentList/>
</>
);
}
export default App;
일반 CSS 사용
제공되는 css
외부 CSS 라이브러리 이용
Bootstrap.jsx
export default function Bootstrap(props){
return(
<form>
<div className="mb-3">
<label htmlFor="exampleInputEmail1" className="form-label">
Email Address
</label>
<input type='email' className="form-control"
id = 'exampleInputEmail1' area-aria-describedby="emailHelp" />
<div id = 'emailHelp' className="form-text">
이메일을 절대로 다른 용도로 사용하지 말것.
</div>
<button type='submit' className="btn btn-primary">
로그인
</button>
</div>
</form>
)
}
App.js
import './App.css';
import Bootstrap from './Bootstrap';
function App() {
return (
<>
<Bootstrap/>
</>
);
}
export default App;
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
src 디렉토리의 index.css를 수정
@import url('https://fonts.googleapis.com/icon?family=Material+Icons');
.material-icons{
font-family: 'Material Icons';
display: inline-block;
}
Icon.jsx
export default function Icon(){
return(
<>
<h3>Icon</h3>
<span className="material-icons">home</span>
<span className="material-icons">check_circle_outline</span>
</>
)
}
App.js
import './App.css';
import Bootstrap from './Bootstrap';
import Icon from './pages/Icon';
function App() {
return (
<>
<Icon/>
<Bootstrap/>
</>
);
}
export default App;
이전 방법의 문제점
다른 사이트의 호스팅 된 외부 CSS 파일을 사용하는데 이 방식은 네트워크에 영향을 받을 수 있음
따라서 설치를 해서 사용하게 되면 설치할 때만 네트워크가 연결되어 있으면 된다.
설치 방법 : npm i @fontsource/글꼴이름
폰트 설치 : npm i @fontsource/material-icons
Icon.jsx
export default function Icon(){
return(
<>
<h3>Icon</h3>
<span className="material-icons">home</span>
<span className="material-icons">check_circle_outline</span>
</>
)
}
components 디렉토리의 모든 내용을 export하는 index.js 파일을 생성하고 작성
index.js
export * from './Icon';
Usingicon.jsx
// 디렉토리 이름을 기재하면 index.js 에서 export한 데이터를 가져온다
import {Icon} from '../components';
export default function UsingIcon(){
return(
<>
<h3>설치한 아이콘</h3>
<Icon name='home' style={{color:'blue'}} />
<Icon name='check_circle_outline' style={{color:'red',fontSize:'70px'}} />
</>
)
}
App.js
import './App.css';
import Bootstrap from './Bootstrap';
import Icon from './pages/Icon';
import UsingIcon from './pages/Usingicon';
function App() {
return (
<>
<UsingIcon/>
<Icon/>
<Bootstrap/>
</>
);
}
export default App;
css 를 불러와서 사용할 때 webpack 이 css-loader를 이용해서 고유한 이름을 만들어 사용하도록 해주는 기술
클래스 이름이 중첩되는 현상을 방지해 준다
[파일이름][클래스이름][해시값] 의 형태로 고유한 클래스 이름을 만들어 준다
모든 곳에서 사용할 수 있는 클래스를 만들 대는 앞에 :global 을 추가해주면 된다
CSSModule.module.css
.wrapper{
background: black;
padding: 1em;
color: white;
font-size: 2rem;
}
:global .something{
font-weight: 800;
color: aquamarine;
}
CSSModule.jsx
import styles from './CSSModule.module.css';
export default function CSSModule (){
return (
<div className={styles.wrapper}>
Hi! I'm <span className='something'>CSS Module~!</span>
</div>
)
}
App.js
import './App.css';
import Bootstrap from './Bootstrap';
import Icon from './pages/Icon';
import UsingIcon from './pages/Usingicon';
import CSSModule from './CSSModule';
function App() {
return (
<>
<CSSModule/>
<UsingIcon/>
<Icon/>
<Bootstrap/>
</>
);
}
export default App;
CSSModule.module.css
.wrapper{
background: black;
padding: 1em;
color: white;
font-size: 2rem;
}
.inverted{
color: black;
background: white;
border: 1px solid burlywood;
}
:global .something{
font-weight: 800;
color: aquamarine;
}
CSSModule.jsx
import styles from './CSSModule.module.css';
export default function CSSModule (){
return (
<div className={`${styles.wrapper} ${styles.inverted}`}>
하이 HI ! I'm <span className='something'>CSS Module~!</span>
</div>
)
}
여러 개의 클래스 이름을 중첩해서 사용하거나 조건부로 사용할 때 편리한 클래스 이름 관련 라이브러리
외부 라이브러리라서 설치 필요
사용 방법
import classNames from 'classnames/bind'
const cx = className.bind(스타일 객체);
CSSModule.jsx 를 수정
CSSModule.jsx
import styles from './CSSModule.module.css';
import classNames from 'classnames/bind';
const cx = classNames.bind(styles);
export default function CSSModule (){
return (
<div className={cx('wrapper', 'inverted')}>
하이 HI ! I'm <span className='something'>CSS Module~!</span>
</div>
)
}
preprocessor : 원래의 기능이 동작하기 전에 수행되는 기능
css preprocessor : css 의 불편함을 해소하기 위해서 등장 (css 작성이 생각보다 어려움)
선택자의 중첩 문제나 조건문, 반복문 등의 사용을 위해서 활용
webpack 이 필요한 이유가 이 부분 때문
대표적인 전처리기로는 Sass, Less, Sylus 등이 있음
Syntactically Awesome Style Sheets 의 약자
도큐먼트 : https://sass-guidelin.es/ko/
❗️❗️❗️ node-scss 패키지 설치 오류 ❗️❗️❗️
$ npm uninstall node-scss
$ npm install -D node-sass
/* 자동으로 고유해질 것이므로 흔히 사용되는 단어를 클래스 이름으로 마음대로 사용가능*/
.wrapper {
background: black;
padding: 1rem;
color: white;
font-size: 2rem;
//inverted 가 wapper 와 같이 사용되는 경우만 적용
& .inverted {
color:black;
background:white;
border: 1px solid black;
}
}
/* 글로벌 CSS 작성 */
:global {
.something{
font-weight: 800;
color: aqua;
}
}
import styles from './CSSModule.module.scss';
import classNames from 'classnames/bind';
const cx = classNames.bind(styles);
export default function CSSModule (){
return (
<div className={cx('wrapper', 'inverted')}>
하이 HI ! I'm <span className='something'>CSS Module~!</span>
</div>
)
}
.box {
display: inline-block;
width: 100px;
height: 100px;
border: 1px solid black;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
&.blue{
background: blue;
}
&:hover {
background: yellow;
}
&:active {
background: red;
}
.box-inside {
background: black;
width: 50px;
height: 50px;
}
}
import React, {Component} from 'react'
import styles from './App.scss';
import classNames from 'classnames/bind';
const cx = classNames.bind(styles)
class App extends Component{
render(){
const isBlue = true;
return(
<div className={cx('box', {blue:isBlue})}>
<div className={cx('box-inside')} />
</div>
)
}
}
export default App;
$size: 100px;
@mixin place-at-center(){
top:50%;
left:50%;
transform: translate(-50%, -50%);
}
@import './styles/util.scss';
.box {
display: inline-block;
width: $size;
height: $size;
border: 1px solid black;
position: fixed;
@include place-at-center();
&.blue{
background: blue;
}
&:hover {
background: yellow;
}
&:active {
background: red;
}
.box-inside {
background: black;
width: 50px;
height: 50px;
}
}
종류
반응형 웹 디자인 (디바이스나 뷰의 크기에 상관없이 동일한 콘텐츠를 사용할 수 있도록 디자인) 을 도와주는 라이브러리 : include-media
색상 설정을 쉽게 해주는 라이브러리 : open-color
$ npm install include-media open-color sass
@import '~open-color/open-color';
@import '~include-media/dist/include-media';
$breakpoints: (
small: 376px,
medium: 768px,
large: 1024px,
huge: 1200px
);
$size: 100px;
@mixin place-at-center(){
top:50%;
left:50%;
transform: translate(-50%, -50%);
}
import styles from './Button.scss';
import classNames from 'classnames/bind';
const cx = classNames.bind(styles);
const Button = ({children, ...rest}) => {
return (
<div className={cx('button')} {...rest}>
{children}
</div>
);
};
export default Button;
@import '/Users/euijoolee/Desktop/React/testapp1/src/styles/Util.scss';
.button {
background: $oc-green-7;
transition: all .2s ease-in;
display: inline-block;
padding-top: 2rem;
padding-bottom: 2rem;
text-align: center;
color: white;
position: fixed;
font-size: 2rem;
font-weight: 500;
border-radius: 4px;
cursor: pointer;
@include place-at-center();
width: 1200px;
// 반응형 디자인
@include media("<huge") {
width: 1024px;
}
@include media("<large") {
width: 768px;
}
@include media("<medium") {
width: 90%;
}
// 마우스 상태에 따라 다른 효과 지정
&:hover {
background: $oc-green-6;
}
&:active {
margin-top: 3px;
background: $oc-green-8;
}
}
import Button from './Button';
export default Button;
import React, {Component} from 'react'
import Button from '/Users/euijoolee/Desktop/React/testapp1/src/button'
class App extends Component{
render(){
return(
<div>
<Button>버튼</Button>
</div>
)
}
}
export default App;