본문 바로가기
Frontend/React

React: 컴포넌트에 CSS 적용하기

by 코딩쥐 2024. 8. 28.

React에서 CSS를 적용하는 방법은 인라인 스타일 적용, CSS 파일 작성 후에 해당 컴포넌트에 파일을 import 하는 경우 그리고 CSS-module을 사용하는 방법이다. 

 

인라인 스타일

인라인 스타일의 경우 가장 간단하고 쉬운 방법이다. HTML의 인라인 스타일처럼 스타일을 적용할 대상에 직접 작성하는 방법이다. CSS와 몇 가지 다른 점이 있다면, style 속성값에 일반 문자열이 아닌 객체가 할당되어야 하며 CSS속성명이 CamelCase로 작성되어야 한다. (background-color -> backgroundColor) 

 

아래 예제처럼 인라인에 바로 작성할 수도 있고, style을 따로 변수를 선언 후 객체를 만들어 스타일에 적용할 수 있다.

function App() {
  const style = {
    width: "100vw",
    height: "90vh",
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    backgroundColor : "lightgray"
  }
  return (
    <div style={style}>
      <p style={{ color: "white", fontSize: "2rem" }}> 인라인 형식으로 적용 </p>
    </div>
  )
}

export default App

 


CSS파일

별도의 파일에 스타일을 정의해놓고 React 컴포넌트에 해당 CSS파일을 import한 후에, 각 요소들의 className 속성을 이용해서 외부 파일에 정의된 스타일을 적용하는 방법이다. 이 경우 상위 컴포넌트의 CSS파일에서 중복 className이 있을 경우 원하지 않은 결과가 나올 수 있다는 단점이 있다. 

 

App.jsx 

import "./App.css";

function App() {
  return (
    <div className="box">
      <p className="p1">CSS파일 import</p>
    </div>
  )
}

export default App

 

App.css

.box {
  display: flex;
  width: 100vw;
  height: 90vh;
  background-color: rgb(249, 249, 249);
  justify-content: center;
  align-items: center;
}

.p1 {
  font-size: 2rem;
  color: lightcoral;
  text-shadow: 2px 2px 1px rgb(159, 226, 255);
}


CSS Module

인라인 스타일의 스타일의 재사용이 어렵고, CSS파일의 경우 클래스 이름의 충돌 위험이 있다. 스타일의 독립성을 보장하고 중복 문제를 방지하기 위해 CSS 파일에 고유의 네임 스페이스를 부여해주는 CSS Module를 사용하는 것을 추천한다. 

  • 외부 스타일 시트를 작성할 때 확장자를 .css가 아닌 .module.css를 사용한다. 
  • React 컴포넌트 파일
    - import 모듈이름 from "./스타일시트이름.module.css";로 CSS모듈 이름을 작성한다.
    - className = {모듈이름.클래스명} 으로 스타일을 적용한다.

이렇게 적용한 후에 요소를 확인해보면 클래스 이름에 해시가 생성되어 클래스 이름 충돌이 방지된 것을 볼 수 있다.

 

CSS Module 예제

컴포넌트 합성에서 예제로 만들었던 TodoList를 CSS Module을 이용해서 스타일을 적용해보았다.

 

1. TodoApp.module.css 

- 전체적인 배치에 대한 스타일 지정을 시행하였다. 

 

TodoApp.jsx

import style from './TodoApp.module.css';

function TodoApp() {
// 모든 내용은 삭제하고 className을 이용해서 CSS모듈을 적용하는 부분만 가져왔다. 
  return (
    <div className={style.container}>
      <div className={style.inp}>
        <TodoInput handleAddTodo={addTodo} />
      </div>
      <div className={style.item}>
        {todos.map((todo, index) => (
          <TodoItem/>
        ))}
      </div>
    </div>
  );
}

export default App;

 

TodoApp.module.css 

.container{
    display: grid;
    grid-template: 
    '... ... ...' 30px
    '... inp ...' 50px
    '... ... ...' 50px
    '... todo ...' 1fr
    '... ... ...' 1fr/ 1fr 400px 1fr;
    background-color: lightgray;
    height: 100vh;
}

.item{
    grid-area: todo;
    overflow-y: auto;
    overflow-x: hidden;
    height: 80vh;
}

.inp{
    grid-area: inp;
}

 

2. TodoInput.module.css 

- 할 일을 등록하는 input 창과 버튼에 대한 스타일을 지정하여 적용하였다. 

 

TodoInput.jsx

import style from './css/TodoInput.module.css'

export default function TodoInput(){
    return(
        <div className={style.div}>
            <input className={style.input} type="text"/>
            <button className={style.button}>추가</button>
        </div>
    )
}

 

 

TodoInput.module.css

.div {
    display: flex;
    flex-direction: column;
    justify-content: center;
    flex-grow: 1;
    margin-bottom: 10px;
}

.input{
    border: none;
    outline: none;;
    margin-bottom: 10px;
    padding: 5px;
}

.input:hover{
    box-shadow: 1px 1px 5px rgb(150, 150, 150)
}

.button{
    border: none;
}

.button:hover{
    background-color: rgb(203, 203, 203);
    box-shadow: 1px 1px 5px rgb(150, 150, 150)
}


3. TodoItem.module.css 

- 등록된 할 일을 출력하는 p태그 그리고 버튼 3개에 대한 스타일을 지정하였다. 

 

TodoItem.jsx

import style from './css/TodoItem.module.css';

export default function TodoItem() {
    return (
        <div>
            {isEditable ?
                <TodoEdit/>
                :
                <div className={style.container}>
                    <p className={style.p} style={{ textDecoration: todo.isCompleted ? "line-through" : "none" }}>{todo.text}</p>
                    <button className={style.btn1}}>수정</button>
                    <button className={style.btn2}}>완료</button>
                    <button className={style.btn3}}>삭제</button>
                </div>
            }
        </div>
    )
}

 

TodoItem.module.css

.container {
    display: grid;
    grid-template:
        '... txt ... btn1 ... btn2 ... btn3' 1fr / 1fr 200px 1fr 50px 10px 50px 10px 50px;
    align-items: center;
    margin-bottom: 10px;
    padding: 10px;
    border-top: 1px solid rgb(85, 85, 85);
    border-bottom: 1px solid rgb(85, 85, 85);
}

.p {
    grid-area: txt;
}

.btn1, .btn2, .btn3{
    border: none;
    border-radius: 5px;
    height: 30px;
}

.btn1:hover, .btn2:hover, .btn3:hover{
    background-color: rgba(187, 187, 187, 0.659);
    box-shadow: 1px 1px 5px rgb(150, 150, 150);
}

.btn1 {
    grid-area: btn1;
}

.btn2 {
    grid-area: btn2;
}

.btn3 {
    grid-area: btn3;
}

 


4. TodoEdit
TodoEdit의 경우 TodoInput과 스타일을 동일하게 사용할 예정이어서, TodoInput.module.css를 가져와 사용하였다.

import style from './css/TodoInput.module.css'

export default function TodoEdit() {
    return (
        <div className={style.div}>
            <input className={style.input}/>
            <button className={style.button}}>수정완료</button>
        </div>
    );
}