3일차 스터디 - Styled Components
🎨 Styled Component
💡 Styled Component란?
JavaScript 기반 라이브러리입니다.
태그가 지정된 템플릿 리터럴( 1일차)과 CSS의 힘을 활용해 구성 요소의 스타일을 지정할 수 있습니다.
또한 구성 요소와 스타일 간의 매핑을 제거합니다.
구성 요소를 낮은 수준의 스타일 구성으로 사용하는 것이 이보다 더 쉬울 수는 없습니다!
🗂 기초
import styled from 'styled-component';
const Title = styled.div`
font-size: 20px;
`;
export default () => <Title>제목</Title>;
태그가 지정된 템플릿 리터럴을 사용하여 Compnent의 스타일을 지정합니다.
Prop 사용
import styled from 'styled-component';
const Title = styled.div`
font-size: 20px;
color: ${props => props.primaty ? 'blue' : 'white'};
`;
export default () => <Title primary>제목</Title>;
스타일이 지정된 Component의 템플릿 리터럴에 함수를 전달하여 props
를 기반으로 적용할 수 있습니다.
함수는 props 라는 매개변수를 가지며 완전한 JavaScript 문법을 사용합니다.
위 예시는 primary
라는 prop
을 전달해 글자 색을 변경합니다.
스타일 확장
import styled from 'styled-component';
import Title from './Title';
const ToamtoTitle = styled(Title)`
background-color: tomato;
`;
export default () => <ToamtoTitle primary>제목</ToamtoTitle>;
일반적 tag가 아닌 Component의 스타일을 재 구성 할 수 있습니다.
기존 styled.div
가 아닌 styled()
를 사용한다는 점을 유의합니다.
위 예시는 기존 Title Component 에 배경색을 tomato로 재 구성합니다.
SCSS
styled-component 의 css문법은 scss문법을 따르기 때문에 scss의 문법들을 사용할 수 있습니다
scss 문법은 나중에 따로 스터디로 올려드리도록 하겠습니다.
(알고계시면 사용해도 되지만 모르셔도 일반 css 처럼 사용하셔도 무방합니다)
Global Style
createGlobalStyle
을 사용하여 전역으로 쓰이는 스타일을 정의할 수 있습니다.
// GlobalStyle.js
const GlobalStyle = createGlobalStyle`
body {
background-color: black;
}
`;
// App.js 안에
<>
<GlobalStyle />
<Header />
</>
API 구성 방식
API 호출이 필요한 컴포넌트에서 모두 axios
를 사용한다면 비효율적인 로직이 반복될것입니다.
그래서 비슷한 성격을 가진 URL 또는 데이터들을 묶어서 관리한다면 추후 유지/보수에서 좋은 로직이 될 수 있습니다.
// api.js
import axios from 'axios';
const api = axios.create({
baseURL: 'https://www.api.com',
});
const users = () => api.get('/user');
const user = (name) => api.get(`/user/${name}`);
export {users, user};
위의 예시처럼 같은 baseURL
을 가지는 api를 하나의 axios
객체를 생성하고 각 데이터를 가져오는 함수들을 만들어 모듈화합니다.
PropTypes
PropTypes를 사용하기위해서는 prop-types 라이브러리를 설치해야 합니다.
TypeScript를 사용하지 않으면 Type 확인을 하지 못해 많은 버그들이 생길 가능성이 있습니다.
이때 React에 내장되어있는 PropTypes
를 사용하여 컴포넌트의 props
타입확인을 할 수 있습니다.
import React from 'react';
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: PropTypes.string
};
PropTypes 속성
MyComponent.propTypes = {
// prop이 특정 JS 형식임을 선언할 수 있습니다.
// 이것들은 기본적으로 모두 선택 사항입니다.
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 랜더링 될 수 있는 것들은 다음과 같습니다.
// 숫자(numbers), 문자(strings), 엘리먼트(elements), 또는 이러한 타입들(types)을 포함하고 있는 배열(array) (혹은 배열의 fragment)
optionalNode: PropTypes.node,
// React 엘리먼트.
optionalElement: PropTypes.element,
// prop가 클래스의 인스턴스임을 선언할 수 있습니다.
// 이 경우 JavaScript의 instanceof 연산자를 사용합니다.
optionalMessage: PropTypes.instanceOf(Message),
// 열거형(enum)으로 처리하여 prop가 특정 값들로 제한되도록 할 수 있습니다.
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 여러 종류중 하나의 종류가 될 수 있는 객체
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 특정 타입의 행렬
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 특정 형태를 갖는 객체
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// 위에 있는 것 모두 `isRequired`와 연결하여 prop가 제공되지 않았을 때
// 경고가 보이도록 할 수 있습니다.
requiredFunc: PropTypes.func.isRequired,
}
Children
// 기본 children의 propTypes
MyComponent.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
])
}
// 하나의 children만 정의
MyComponent.propTypes = {
children: PropTypes.element.isRequired
}
Container Presenter Pattern
작은 Application일때는 크게 상관 없지만 Application 이 점차 커지는 경우 Container Presenter Pattern이 유리하다.
Container
컨테이너는 데이터와 상태값(state)을 가지고 API를 불러온 뒤 로직을 처리한다.
// HomeContainer.js
import React from "react";
import CoinsPresenter from "./CoinsPresenter";
export default class HomeContainer extends React.Component {
state = {
// your state
};
async componentDidMount() {
// your logic
}
render() {
const {data} = this.state;
return <CoinsPresenter data={data} />;
}
}
Presenter
컨테이너에서 보내준 데이터를 보여주는 역할을 한다.
상태값을 가지고 있지 않다.
API도 없고 클래스도 없고 그냥 함수형 컴포넌트이다.
// HomePresenter.js
import React from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
const Container = styled.div`
/* your styles */
`;
const HomePresenter = ({ data }) => <Container>{data}</Container>
ExchangesPresenter.propTypes = {
data: PropTypes.string.isRequired,
};
export default HomePresenter;
파일 구성
- Home
- HomeContainer.js
- HomePresetner.js
- index.js
index.js
에서 HomeContainer.js
를 export
해주는것으로 인해
import Home from 'screens/Home';
으로 HomeContainer
는 상태값(state)을 가진 모든 리엑트 컴포넌트가 된다.
과제
아래와 같은 결과물이 나오면 됩니다!
- Container Presenter 패턴을 사용해야 합니다.
- Container는 모두 Class Component로 구성되어야 합니다.
- React Hooks를 사용하지 마세요. 추후에 스터디 후 사용할 예정입니다.
- Class Component 를 사용하여 생명주기 함수들을 이용해야 합니다.
- styled-component 만 이용하여 스타일을 구성해야 합니다.
- GlobalStyles 를 사용해야 합니다.
- api 호출은 axios를 그대로 사용하면 안됩니다
- axios.create() 를 이용하여 객체를 만들어야 합니다.
- api 호출하는 함수를 구성하여 export 하여 사용해야 합니다.
- 모든 Presenter Component 들은 propTypes 를 구성해야 합니다.
- Loader Component를 구성해야 합니다.
API URL
- baseURL: https://api.coinpaprika.com/v1
- price: /tickers
- exchages: /exchanges
- coins: /coins