[React] 컴포넌트 스타일링

summereuna🐥·2023년 3월 22일
0

React JS

목록 보기
39/69

리액트로 웹앱을 구축할 때, 단지 컴포넌트를 구상하는 것 뿐만 아니라 로직이 제대로 작동하도록 해야 한다.
리액트 앱을 구축하는 것은 또한 앱을 스타일링 하는 것이기도 하다. 스타일링은 컴포넌트를 구축하는데 있어서 아주 중요한 부분이기도 하다.

  • 동적으로 스타일링하는 다양한 방법
  • 특정 컴포넌트의 스타일에 영향을 받지 않도록 다른 컴포넌트를 스타일링하는 방법

🎯 목표

조건부로 혹은 동적인 방법(Conditional @ Dynamic Styles)으로 스타일을 설정하는 방법에 대해 알아보자.
(ex. 사용자가 유효하지 않은 입력값을 넣으면 입력 필드가 빨간색으로 표시됨)
1. styled Components
서드 파티 라이브러리인 styled Components를 통해, 고유한 범위를 가진 스타일로 미리 스타일이 지정된 컴포넌트 설정할 수 있다.
2. css modules

🌟 예제

현재 사용자가 공백만 입력하더라도 투두리스트에 항목이 추가가 되고 있다. 이를 고치기 위해 공백으로 입력시 추가하지 못하게 만들어보자. 그리고 그에 더해 공백만 입력할 경우 사용자에게 유효하지 않음을 표시할 수 있도록 스타일을 바꿔보자.

1. 공백 입력시 투두리스트 추가하지 못하게 하기

먼저 공백으로 입력시 추가하지 못하게, 폼에서 입력 값의 길이를 체크하여 이 값이 유효한지 체크한다.

const formSubmitHandler = (event) => {
  event.preventDefault();
  if (enteredValue.trim().length === 0) {
    setIsValid(false);
    return;
  }
  props.onAddGoal(enteredValue);
};
  • enteredValue.trim().length === 0
    입력된 값이 유효한지를 먼저 체크 해야한다.
    trim() 메소드를 사용하여 사용자가 공백만 입력하여 입력값이 비었으면, 유효하지 않기 때문에 isValid의 상태를 false로 바꿔 주고, 반환return하여 함수 실행을 중단시키면 된다.

    trim() 메소드는 시작이나 끝 부분에 과도하게 쓰인 공백 제거해주는 메소드로, 사용자가 수많은 공백(띄워쓰기만)을 입력한 경우 유효성을 배제하기 위해 사용할 수 있다.

2. 스타일 주기

사용자에게 피드백 주기 위해, 입력값이 유효하지 않은 경우 입력 부분에 빨간색 테두리와, 연한 빨간색 바탕 추가하고, 레이블도 빨간색으로 바뀌게 해보자.

  • state를 사용하면된다. state는 사용자가 입력한 것이 유효한지 나타내는 지표가 되므로 불리언으로 작업하면 된다.
    isValid의 상태가 false일때, 사용자에게 시각적으로 유효하지 않다는 표시를 주기 위해 스타일을 적용한다.

✅ 1. 동적으로 inline style 설정하기

  • 스타일을 추가하는 가장 쉬운 방법은, 레이블에 inline으로 스타일 객체를 추가하는 것이다.
    • style={{ color: !isValid ? "red" : "black" }}
      isValid가 false여서 !isValid가 true가 될때만 "red"가 되도록 작성하면 된다.
  • 하지만 이렇게 inline으로 작성하면, css에 최우선 순위를 두기 때문에 우리의 모든 스타일을 오버라이드해버린다는 단점이 있다.
import React, { useState } from "react";

import Button from "../../UI/Button/Button";
import "./CourseInput.css";

const CourseInput = (props) => {
  const [enteredValue, setEnteredValue] = useState("");
  const [isValid, setIsValid] = useState(true);

  const goalInputChangeHandler = (event) => {
    //2. 입력 값이 다시 유효해져야 하기 때문에, 리셋 기능을 설정하기 위해 모든 키 입력에 반응하는 위치로 가야 한다.
    if (event.target.value.trim().length > 0) {
      //입력값이 유효하다면 isValid상태를 true로 설정하여
      setIsValid(true);
    }
    setEnteredValue(event.target.value);
  };

  const formSubmitHandler = (event) => {
    event.preventDefault();
    /*
    1. 공백으로 입력시 목표 추가 못하게 하기
    입력된 값이 유효한지 체크 먼저 해야함
    trim()메소드는 시작이나 끝 부분에 과도하게 쓰인 공백 제거해주는 메소드
    사용자가 수많은 공백(띄워쓰기만)을 입력한 경우 배제하기 위해 사용함
    */
    //길이 체크
    if (enteredValue.trim().length === 0) {
      /*
      입력값이 비었으면 그냥 반환해서 함수 실행 중단시키기
      사용자에게 피드백 주기 위해
      입력 부분에 빨간색 테두리와, 연한 빨간색 바탕 추가하고, 레이블도 빨간색으로 바뀌게 하기
      */
      //state를 사용하면 된다. 그 state는 사용자가 입력한 것이 유효한지 나타내는 지표가 되므로 불리언으로 작업하자.
      setIsValid(false);
      //isValid가 false면 스타일 적용
      // 쉬운 방법 1. 레이블에 inline으로 객체 추가하기
      //style={{ color: !isValid ? "red" : "black" }}
      //isValid가 false여서 !isValid가 true가 될때만 "red"가 되도록
      //하지만 이렇게 inline으로 작성하면, css에 최우선 순위를 두기 때문에 우리의 모든 스타일을 오버라이드한다.
      return;
    }
    props.onAddGoal(enteredValue);
  };

  return (
    <form onSubmit={formSubmitHandler}>
      <div className="form-control">
        <label style={{ color: !isValid ? "red" : "black" }}>Course Goal</label>
        <input
          style={{
            borderColor: !isValid ? "red" : "",
            backgroundColor: !isValid ? "salmon" : "transparent",
          }}
          type="text"
          onChange={goalInputChangeHandler}
        />
      </div>
      <Button type="submit">Add Goal</Button>
    </form>
  );
};

export default CourseInput;

✅ 2. 동적으로 css class 설정하기

동적으로 css class를 설정하기 위해 먼저 css 파일에 원하는 스타일을 추가한다.

📍 css 파일

.form-control.invalid input {
  border-color: red;
  background-color: #ffd7d7;
}

.form-control.invalid label {
  color: red;
}
  • .form-control.invalid
    form-controlinvalid클래스에 접근 가능하다고 선언한다.
    이는 같은 요소에 있어야 한다는 뜻이다.
    작성할 때, form-control과 invalid 사이에 공백이 없어야 한다.
  • .form-control.invalid input{}
    input을 타겟으로 한다는 뜻이다.

이 css가 효과를 가지려면 invalid 클래스가 동적으로 추가되어야 한다.

📍 js 파일

css 파일에 추가해둔 클래스를 상태에 따라 div 요소에 추가한다.

return (
  <form onSubmit={formSubmitHandler}>
    <div className={`form-control ${!isValid ? "invalid" : ""}`}>
      <label>Course Goal</label>
      <input type="text" onChange={goalInputChangeHandler} />
    </div>
    <Button type="submit">Add Goal</Button>
  </form>
);
  • <div className={}>
    동적인 값을 위해 중괄호를 사용한다.
  • <div className={``}>
    중괄호 안에는 백틱(`)으로 작성한다.
    백틱은 자바스크립트의 기본 기능으로 템플릿 리터럴이라고 불린다. 이는 싱글 따옴표가 아닌 백틱이 있는 문자열을 구성한다. 백틱 사이에 타이핑되는 것은 일반적인 문자열로 취급된다.
<div className={`form-control ${!isValid ? "invalid" : ""}`}
  • isValid가 false인 경우에만 "invalid" 클래스를 추가하도록 한다.
  • 백틱 사이에서 ${}를 작성하고 중괄호 사이에 컨텐츠를 넣으면 문자열에 동적인 값을 주입할 수 있다.
    문자열에 컨텐츠를 추가하여 중괄호 사이에 전달하는 모든 컨텐츠는 자바스크립트 표현식이 될 수 있다.

이 방법은 css파일로 작업할 때, 클래스만으로도 작업할 수 있는 방법이다.
간단한 구문으로 동적으로 클래스를 추가하거나 삭제할 수 있어 편리하다.

DOM에서 클래스를 추가하거나 제거하는 것은 리액트가 하는 일이다.
내가 할 일은,

  1. 목표를 정하고 (form-control만 가지는지, 아니면 form-control invalid를 가지는지)
  2. 대체할 수 있는 state를 지정하여 원하는 스타일을 동적으로 추가하여 출력되게 하기

그러면 리액트는 그에 맞춰 업데이트 해준다.

✅ 3. styled 컴포넌트에 props 추가하여 백틱안에 props 사용하기

백틱안에 props을 사용하면 스타일을 손쉽게 동적으로 바꿀 수 있다.
<FormControl invalid={!isValid}>

import React, { useState } from "react";

import Button from "../../UI/Button/Button";
//import "./CourseInput.css";
import styled from "styled-components";

//JSX에서 컴포넌트로 사용할 것이기 때문에 대문자로 작성
//스타일 컴포넌트 패키지 사용: JSX의 div 부분 대체

//스타일컴포넌트에 전달되는 일부 props에 따라 스타일을 동적으로 바꿀 수 있다.
const FormControl = styled.div`
  margin: 0.5rem 0;

  & label {
    font-weight: bold;
    display: block;
    margin-bottom: 0.5rem;
    color: ${(props) => (props.invalid ? "red" : "black")};
  }

  & input {
    display: block;
    width: 100%;
    border: 1px solid ${(props) => (props.invalid ? "red" : "#ccc")};
    background: ${(props) => (props.invalid ? "#ffd7d7" : "transparent")};
    font: inherit;
    line-height: 1.5rem;
    padding: 0 0.25rem;
  }

  & input:focus {
    outline: none;
    background: #fad0ec;
    border-color: #8b005d;
  }
`;

const CourseInput = (props) => {
  const [enteredValue, setEnteredValue] = useState("");
  const [isValid, setIsValid] = useState(true);

  const goalInputChangeHandler = (event) => {
    //2. 입력 값이 다시 유효해져야 하기 때문에, 리셋 기능을 설정하기 위해 모든 키 입력에 반응하는 위치로 가야 한다.
    if (event.target.value.trim().length > 0) {
      //입력값이 유효하다면 isValid상태를 true로 설정
      setIsValid(true);
    }
    setEnteredValue(event.target.value);
  };

  const formSubmitHandler = (event) => {
    event.preventDefault();
    //1. 공백으로 입력시 목표 추가 못하게 하기: 길이 체크
    if (enteredValue.trim().length === 0) {
      //입력값이 비었으면 그냥 반환해서 함수 실행 중단시키.
      //사용자가 입력한 것이 유효한지 나타내는 지표가 되는 state를 불리언으로 사용
      setIsValid(false);
      return;
    }
    props.onAddGoal(enteredValue);
  };

  return (
    <form onSubmit={formSubmitHandler}>
      //🔥
      <FormControl invalid={!isValid}>
        <label>Course Goal</label>
        <input type="text" onChange={goalInputChangeHandler} />
      </FormControl>
      <Button type="submit">Add Goal</Button>
    </form>
  );
};

export default CourseInput;
profile
Always have hope🍀 & constant passion🔥

0개의 댓글