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;

파일 구성

index.js에서 HomeContainer.jsexport 해주는것으로 인해

import Home from 'screens/Home'; 으로 HomeContainer는 상태값(state)을 가진 모든 리엑트 컴포넌트가 된다.

과제

스터디 3일차 과제

아래와 같은 결과물이 나오면 됩니다!

  1. Container Presenter 패턴을 사용해야 합니다.
  2. Container는 모두 Class Component로 구성되어야 합니다.
    1. React Hooks를 사용하지 마세요. 추후에 스터디 후 사용할 예정입니다.
    2. Class Component 를 사용하여 생명주기 함수들을 이용해야 합니다.
  3. styled-component 만 이용하여 스타일을 구성해야 합니다.
  4. GlobalStyles 를 사용해야 합니다.
  5. api 호출은 axios를 그대로 사용하면 안됩니다
    1. axios.create() 를 이용하여 객체를 만들어야 합니다.
    2. api 호출하는 함수를 구성하여 export 하여 사용해야 합니다.
  6. 모든 Presenter Component 들은 propTypes 를 구성해야 합니다.
  7. Loader Component를 구성해야 합니다.

API URL

  1. baseURL: https://api.coinpaprika.com/v1
    1. price: /tickers
    2. exchages: /exchanges
    3. coins: /coins

3day.gif