백엔드 개발자 프론트 도전기 feat.리엑트 [2]

     

 

SASS라고??

 

리액트를 하다 보니 SASS라는 단어가 나왔다. 풀 네임은 Syntactically Awesome Style Sheets인데 멋진 스타일 시트 정도로 해석할 수 있다 ㅋㅋ

 

SASS는 CSS pre-processor로 약간 코딩하는 방식으로 CSS를 만들면 SASS가 CSS로 변환하는 역할을 해준다. 그래서 SASS로 작성한 문서는 브라우저에서는 단순히 CSS로만 보이게 된다. 초창기에는. sass파일만 지원했는데 지금은. scss파일도 지원한다. 두 개가 다 똑같은 sass 긴 한데. scss가 문법이 좀 더 css와 비슷해서 사용하기 쉽다.

 

 

일단 sass쓰려면 따로 패키지를 설치해야 한다. 

(* 5.0.0 버전이 있긴 한데 작동하지 않는다. 4.14.1을 선택하자)

 

젯브레인형님들의 웹스톰에서 쉽게 패키지 설치 가능. 4.14.1버전으로 설치해야한다

 

 

그리고는. scss파일을 만들어서 입력하면 된다.

아래는 여러가지 버튼 스타일을 저장한. scss파일이다.

 

$blue: #228be6;
$gray: #495057;
$pink: #f06595;

@mixin button-color($color) {
  background: $color;
  &:hover {
    background: lighten($color, 10%);
  }
  &:active {
    background: darken($color, 10%);
  }
}

.Button {
  display: inline-flex;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  border: none;
  cursor: pointer;

  // 사이즈 관리
  &.large {
    height: 3rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1rem;
  }

  &.small {
    height: 1.75rem;
    font-size: 0.875rem;
    padding-left: 1rem;
    padding-right: 1rem;
  }

  // 색상 관리
  &.blue {
    @include button-color($blue);
  }

  &.gray {
    @include button-color($gray);
  }

  &.pink {
    @include button-color($pink);
  }

  & + & {
    margin-left: 1rem;
  }
}

 

@mixin이라는 게 처음 나오는데 css의 함수 같은 느낌이다. 입력 갑을 주면 해당 입력으로 적당한 css 아웃풋을 가져온다.  

 

 

 

classname 패키지

 

아래는 Button.js의 코드인데 classnames란 함수를 사용했다.

css에 class 넣을 때 xxx yyy dd 이런 식으로 여러 개를 넣을 때가 있는데, 컴포넌트의 인자 값으로 들어오는 값들을 나열하기 위해 배열을 쓰지 않고 classnames를 사용해 쓰면 알아서 한 칸씩 띄워서 입력해준다. classname도 패키지만 설치해주면 바로 사용할 수 있다.

 

import React from 'react';
import classNames from 'classnames';
import './Button.scss'

function Button({children, size, color, outline}){
    return (
        <button className={classNames('Button', size, color, outline)} >{children}</button>
    );
}

Button.defaultProps = {
    size: 'medium',
    color: 'blue'
}

export default Button

 

 

classnames라고 검색

 

 

 

CSS Module

 

최근에 프론트 몇 개 만들면서 느낀 것이 class이름이 중복될 수 있다. 그럼 꼬인다 ㅋㅋ

예를 들어. sample이라고 해놓고 p.sample이라고 새로 지정하면. sample은 글로벌이기 때문에 p.sample은 실제로. sample + p.sample이 된다. (당연한 소리지만,,, 처음 하면 모른다고)

 

그밖에도 기존에 개발되어있는 곳에 새로운 css를 넣을 때 이름이 있는지 없는지 잘 찾아봐야 하는데, CSS Module을 사용하면 겹치지 않게 이름을 잘 생성해준다. 

 

 

 

styled-components

 

CSS in JS라고 해서 js에서 CSS를 만드는 기술이다.

(*styled-components 패키지 설치해야 한다)

 

//App.js
import React from 'react';
import styled from 'styled-components';

const Circle = styled.div`
  width: 5rem;
  height: 5rem;
  background: ${props => props.color || 'black'};
  border-radius: 50%;
`;

function App() {
    return <Circle color="blue"/>;
}

export default App;

 

위처럼 .js에서 css를 활용한 스타일을 적용할 수 있다.

 

styled-components에 ThemeProvider라는 함수도 제공하는데, html 라인에서 palette에서 색을 빼올 수 있다. 물론 이렇게 간단하게 쓰지 않고 전체적으로 관리하는 공통 css를 theme로 관리해서 사용할 수도 있다.

공식 홈피 - styled-components: Advanced Usage (styled-components.com)

 

styled-components: Advanced Usage

Theming, refs, Security, Existing CSS, Tagged Template Literals, Server-Side Rendering and Style Objects

styled-components.com

 

 

 

Todo 만들어보기

 

너무 기본 문법만 배워서 잘 모르겠다. 어떻게 쓰는지도 모르겠고, 맨땅에 만들 수 있을지도 모르겠고 ㅋㅋㅋ

일단 뭐라도 만들어보면서 해야지

 

 

이걸 만든다

 

일단 예제에서는 프론트로만 ToDo를 만들었는데, 궁극적인 목표는 뒤에 백엔드를 붙이는 것이다.

 

먼저 react-icons, styled-components 패키지를 설치하고 시작.

 

 

먼저 ui부터 그렸는데, 디자인이 없이 시작하려니 단순히 코드를 복붙 해서 가져오는 것밖에 안 하고 있어서 뭔가 이상하다. 

 

실제로는 제플린을 보면서 한줄한줄 쳐서 화면을 완성했는데, 이거 뭐 기획 없고 디자인 없고 바로 화면 그릴려니 ㅋㅋ

잘하는 FE개발자들은 바로바로 나오려나..?

 

암튼 화면은 일단 그림 

(화면까지 소스 : tkdlek11112/todo-list at 화면UI (github.com))

 

화면까지 완성

 

 

Context API를 다시 한번 복습해보자면 상태를 관리하기 위해 useReducer를 만들었고 Context로 만들어서 Provider로 감싸면 그 안에서는 Context를 사용할 수 있었다.

 

function todoReducer(state, action){
    switch (action.type){
        case 'CREATE':
            return state.concat(action.todo);
        case 'TOGGLE':
            return state.map(todo => todo.id === action.id ? {...todo, done: !todo.done} : todo);
        case 'REMOVE':
            return state.filter(todo => todo.id !== action.id);
        default:
            throw new Error('정의되지 않은 액션이 들어왔어욤');
    }
}

const TodoStateContext = createContext();
const TodoDispatchContext = createContext();

export function TodoProvider({children}){
    const [state, dispatch] = useReducer(todoReducer, initialTodos);
    return (
        <TodoStateContext.Provider value={state}>
            <TodoDispatchContext.Provider value={dispatch}>
                {children}
            </TodoDispatchContext.Provider>
        </TodoStateContext.Provider>
    );
}

 

이렇게 reducer를 만들고 createContext를 이용해서 State와 Dispatch를 만들어서 Provider value로 넘겨주면 다른 곳에서 state와 dispatch를 사용해 context를 사용할 수 있다.

 

import React, { useContext } from 'react';
import { TodoStateContext, TodoDispatchContext } from '../TodoContext';

function Sample() {
  const state = useContext(TodoStateContext);
  const dispatch = useContext(TodoDispatchContext);
  return <div>Sample</div>;
}

 

 

근데 js에서 제일 적은 안 되는 건 list를 뿌릴 때 iter를 돌리는 게 map이라는 함수를 이용해서 돌린다는 것이다. 이게 for문인지 while인지 처럼 직관적이지 않다 ㅋㅋ 뭐 익숙해지면 비슷해지려나..

 

function TodoList(){
    const todos = useTodoState()
    return (
        <TodoListBlock>
            {todos.map(todo => (
                <TodoItem key={todo.id} id={todo.id} text={todo.text} done={todo.done}/>
            ))};
        </TodoListBlock>
    );
}

 

 

 

여차 저차 해서 완성!

 

 

 

 

 

 

 

반응형

댓글(3)

  • hoon
    2021.01.19 15:33

    크 고생하셨습니다.

    일단 map같은 경우에는 리스트 안에 있는 iter이 가능한 element를 for loop처럼 하나씩 가져와서 콜백 함수안에 로직으로 돌리고
    return을 시킨값들을 새로운 array 에 담아서 리턴하는데요,

    react의 jsx같은 경우 리스트 형태의 데이터는 그냥 표현시켜버려서 콤포넌트를 map을 활용한 implicit return해버리면 좀 편하게 뿌릴 수 있어서 활용하는걸로 알고있어요.

    그래서 reducer 같은 경우에도 이전에 사용되던 prevState 들을 변경시키면 안되다 보니 데이터를 추가시키거나 덮어씌울 때 map을 활용하는거구요.

    같은 맥락으로 새로운 투두를 더하는 경우 array.concat() 은 기존의 state을 변경시켜버려서 사용을 지양하고, spread operation을 사용해서 [...state, action.payload] 식으로 기존 state 복제 + 새로운 payload를 푸시 해야 한다고 알고있습니당


    • 2021.01.19 23:28 신고

      ㅋㅋ ㅎㅇ욤

      c++ STL에서 map을 데이터저장소로 사용하는데 여기서는 iterator로 사용해서 이질감이 느껴지네여 ㅋㅋ

      concat도 기존 state를 변경시키지는 안습니다~ 인자값들을 합쳐서 새 배열을 만드는것이기 때문에 기존 state는 안변해욤~

    • 2021.01.21 22:27

      아 맞네요순간 헷갈려씀당 제정신이 아닌듯 해요 ㅋㅋㅋ

Designed by JB FACTORY