프론트엔드(Web)/React

리액트 - 컴포넌트 및 컴포넌트 css 스타일링

만능 엔터테이너 2024. 8. 20. 23:02
728x90
반응형
SMALL

리액트 컴포넌트

리액트는 컴포넌트를 작성할 때 클래스형과 함수형의 두 가지 방식으로 작성할 수 있습니다. 아직까지 두 방식 모두 지원하고 있지만, 공식적으로 리액트에서는 더 이상 클래스 컴포넌트 방식을 사용하지 말기를 권장합니다. 따라서 되도록 클래스 컴포넌트를 배우지 않아도 되지만, 레거시(Legacy) 유지 보수 차원에서 간단하게 살펴봅니다.

함수형 컴포넌트와 클래스형 컴포넌트

리액트 프로젝트를 초기 생성하면 함수형 컴포넌트 방식으로 코드가 작성되어 있습니다. 해당 코드를 리액트 방식으로 변경하면 아래와 같이 변경할 수 있습니다.

 

함수형 컴포넌트

import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  )
}

export default App

 

클래스형 컴포넌트

import { Component } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

interface AppState {
  count: number;
}

class App extends Component<
  React.Dispatch<React.SetStateAction<AppState>>,
  AppState
> {
  constructor(props: React.Dispatch<React.SetStateAction<AppState>>) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  render() {
    const { count } = this.state;
    return (
      <>
        <div>
          <a
            href="https://vitejs.dev"
            target="_blank"
            rel="noopener noreferrer"
          >
            <img src={viteLogo} className="logo" alt="Vite logo" />
          </a>
          <a href="https://react.dev" target="_blank" rel="noopener noreferrer">
            <img src={reactLogo} className="logo react" alt="React logo" />
          </a>
        </div>
        <h1>Vite + React</h1>
        <div className="card">
          <button onClick={() => this.setState({ count: count + 1 })}>
            count is {count}
          </button>
          <p>
            Edit <code>src/App.tsx</code> and save to test HMR
          </p>
        </div>
        <p className="read-the-docs">
          Click on the Vite and React logos to learn more
        </p>
      </>
    );
  }
}

export default App;

 

함수형 컴포넌트의 코드와 비교해보세요. 어떤 게 더 간단한가요? 함수형 컴포넌트의 코드가 훨씬 더 간단하고 직관적임을 알 수 있습니다.

이제 함수형 컴포넌트만 쓰세요 => 리액트 '훅'이 지원되므로 '훅'은 Only 함수형 컴포넌트에서만 지원함 But 클래스형 컴포넌트를 지원하지 않는 것은 아님! 기능은 동일함  ==> Just 함수형 컴포넌트 사용!

함수형 컴포넌트는 리액트 16.8 버전에 추가된 ‘리액트 훅’으로 인해서 사용성의 대변화를 겪게 됩니다. 쉽게 말하면 16.8 이전 버전에서는 함수형 컴포넌트를 사용하는 게 불편했지만, 16.8 부터 새롭게 추가된 ‘리액트 훅’으로 인해서 더 이상 클래스 컴포넌트를 사용하는 게 비효율적인 시대가 되었습니다.

대표적인 리액트 훅에는 다음과 같은 것들이 있습니다.

  • useState
  • useEffect
  • useContext
  • useReducer
  • useRef

이런 리액트 훅은 앞으로 하나씩 배워보겠습니다.

😵 아직 저희 회사는 클래스형 컴포넌트를 사용하는데요? 리액트 버전을 16.8버전 이상으로 올리고 클래스형 컴포넌트를 함수형으로 바꿔서 쓰세요.. 제발요… 네… ? (팀장 멱살 잡는 소리)


컴포넌트를 작성하는 방법

리액트가 폭발적인 성장을 할 수 있었던 여러가지 이유 중 하나에는 ‘쉬운 문법’도 있었습니다. 이러한 쉬운 문법의 일환으로 도입된 게 JSX인데, JSX는 처음부터 있었을까요?

 

HTML 예제

다음과 같은 HTML 예제가 있습니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>HTML 연습용 예제</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        margin: 0;
        padding: 0;
        background-color: #f0f0f0;
      }
      header,
      footer {
        background-color: #333;
        color: white;
        text-align: center;
        padding: 1em 0;
      }
      main {
        padding: 20px;
        background-color: white;
      }
      nav ul {
        list-style: none;
        padding: 20px;
      }
      nav ul li {
        display: inline;
        margin-right: 10px;
      }
      section {
        margin-bottom: 20px;
      }
    </style>
  </head>
  <body>
    <div>
	    <header>
	      <h1>내 웹사이트</h1>
	    </header>
	
	    <nav>
	      <ul>
	        <li><a href="#section1">소개</a></li>
	        <li><a href="#section2">서비스</a></li>
	        <li><a href="#section3">연락처</a></li>
	      </ul>
	    </nav>
	
	    <main>
	      <section id="section1">
	        <h2>소개</h2>
	        <p>여기에 소개 내용을 작성하세요.</p>
	      </section>
	
	      <section id="section2">
	        <h2>서비스</h2>
	        <p>여기에 제공하는 서비스 내용을 작성하세요.</p>
	      </section>
	
	      <section id="section3">
	        <h2>연락처</h2>
	        <p>여기에 연락처 정보를 작성하세요.</p>
	      </section>
	    </main>
	
	    <footer>
	      <p>&copy; 2024 내 웹사이트. 모든 권리 보유.</p>
	    </footer>
   </div>
</body>
</html>

HTML은 위와 같이 굉장히 직관적이고 쉬운 형태로 문법이 구성되어 있습니다.

CreateElement

CreateElement는 리액트가 처음 출시될 때 부터 존재했던 컴포넌트 작성 방법이었습니다.

 

기본 문법

const element = createElement(type, props, ...children);
const divEl = createElement("div", {id:"name"}, "Hello"); // <div id="main">Hello</div>

 

import { createElement } from 'react';

function Greeting() {
  // <h1 class="greeting">Hello, React!</h1>
  return createElement(
    'h1',
    { className: 'greeting' },
    'Hello, React!'
  );
}
import { createElement } from 'react';

function Greeting() {
  // <div>
  //   <h1 class="greeting">Hello, React!</h1>
  // </div>
  return createElement(
    "div",
    null,
    createElement("h1", { className: "greeting" }, "Hello, E")
  );
}
import { createElement } from 'react';

function Greeting() {
  // <div><h1 class="greeting">Hello, React!</h1><p>Welcome to React 18!</div>
  return createElement(
    "div",
    null,
    createElement("h1", { className: "greeting" }, "Hello, React!"),
    createElement("p", null, "Welcome to React 18!")
  );
}
import { createElement } from 'react';

function Greeting({ name }) {
  // <div><h1 class="greeting">Hello, React!</h1><p>Welcome to React 18!</p><ul><li>One</li><li>Two</li><li>Three</li></ul></div>
  return createElement(
    "div",
    null,
    createElement("h1", { className: "greeting" }, "Hello, React!"),
    createElement("p", null, "Welcome to React 18!"),
    createElement(
      "ul",
      null,
      createElement("li", null, "One"),
      createElement("li", null, "Two"),
      createElement("li", null, "Three")
    )
  );
}

연습문제

맨 위에 등장한 HTML 구조를 createElement로 만들어보세요.

import { createElement } from "react";

const App = () => {
  return createElement(
    "div",
    null,
    createElement("header", null, createElement("h1", null, "내 웹사이트")),
    createElement(
      "nav",
      null,
      createElement(
        "ul",
        null,
        createElement(
          "li",
          null,
          createElement("a", { href: "#section1" }, "소개")
        ),
        createElement(
          "li",
          null,
          createElement("a", { href: "#section2" }, "서비스")
        ),
        createElement(
          "li",
          null,
          createElement("a", { href: "#section3" }, "연락처")
        )
      )
    ),
    createElement(
      "main",
      null,
      createElement(
        "section",
        { id: "section1" },
        createElement("h2", null, "소개"),
        createElement("p", null, "여기에 소개 내용을 작성하세요.")
      ),
      createElement(
        "section",
        { id: "section2" },
        createElement("h2", null, "서비스"),
        createElement("p", null, "여기에 제공하는 서비스 내용을 작성하세요.")
      ),
      createElement(
        "section",
        { id: "section3" },
        createElement("h2", null, "연락처"),
        createElement("p", null, "여기에 연락처 정보를 작성하세요.")
      )
    ),
    createElement(
      "footer",
      null,
      createElement("p", null, "&copy; 2024 내 웹사이트. 모든 권리 보유.")
    )
  );
};
export default App;

JSX(Javascript XML)

JSX는 Javascript XML의 약자로 자바스크립트를 확장한 문법입니다. 리액트에서 자바스크립트와 HTML 문법을 조금 더 편하게 작성하기 위해서 리액트 팀에서 고안해낸 새로운 문법 형태입니다.

아래와 같은 코드를 볼까요? 자바스크립트 같으면서도 HTML 같은 애매모호한 코드가 작성되어져 있습니다.

const element = <h1>Hello, world!</h1>;

만약 위에 같은 코드가 자바스크립트 코드여야 한다면 아래와 같아야 했을 겁니다.

const element = "<h1>Hello, world!</h1>";

하지만, 문자열이 아닌 그냥 HTML이 변수에 할당되었습니다. 바로 이게 JSX 문법의 특징이자 장점입니다. JSX를 사용하면 자바스크립트와 HTML 마크업을 하나의 파일에서 편하게 작성할 수 있습니다

 

기본 문법

const element = <h1>Hello, world!</h1>;
function Greeting() {
  // <h1 class="greeting">Hello, React!</h1>
  return <h1 className="greeting">Hello, React!</h1>
}
function Greeting() {
  // <div><h1 class="greeting">Hello, React!</h1></div>
  return return (
    <div>
      <h1 className="greeting">Hello, React!</h1>
    </div>
  );
}
function Greeting() {
  // <div><h1 class="greeting">Hello, React!</h1><p>Welcome to React 18!</div>
  return (
    <div>
      <h1 className="greeting">Hello, React!</h1>
      <p>Welcome to React 18!</p>
    </div>
  );
}
import { createElement } from 'react';

function Greeting({ name }) {
  // <div><h1 class="greeting">Hello, React!</h1><p>Welcome to React 18!</p><ul><li>One</li><li>Two</li><li>Three</li></ul></div>
  return (
    <div>
      <h1 className="greeting">Hello, React!</h1>
      <p>Welcome to React 18!</p>
      <ul>
        <li>One</li>
        <li>Two</li>
        <li>Three</li>
      </ul>
    </div>
  );
}

연습문제

위의 HTML을 JSX로 만들어보세요.

JSX 사용 규칙

JSX는 몇 가지 지켜야 하는 규칙이 있습니다. 살펴봅시다.

반드시 하나의 루트 태그여야 한다

JSX는 반드시 하나의 루트 태그로 구성되어야 합니다.

😵 <></>는 뭐에요??? <></>는 리액트에서 제공하는 Fragment입니다.
아무런 의미도 없이 태그를 묶어주기 위해서 사용합니다.

 

여러 줄의 코드를 리턴할 경우 ‘소괄호’ 사용하기

// Bad
const App = () => {
  return <>
	    <div>App</div>
	    <div>App</div>
	  </>
}

// Good
const App = () => {
  return (
    <>
	    <div>App</div>
	    <div>App</div>
    </>
  )
}

반드시 태그는 닫아준다.

// html4
<br />

// html
<br>
// Bad
const App = () => {
  return (
    <br>
  )
}

// Good
const App = () => {
  return (
    <br/>
  )
}

표현식은 { } 를 사용한다.

템플릿 문자열 기억하시나요?

const yourName = "Chul Su";
const printHello = `Hello, ${yourName}`;

 

JSX도 { } 를 사용해서 표현식을 사용할 수 있습니다.

const App = () => {
  const yourName = "Chul Su";
  return <div>{yourName}</div>;
};

 

class 속성은 className으로

const App = () => {
  return <div className="wrap">Hello, World!</div>;
};

 

함수형 컴포넌트 이름은 항상 대문자로

// bad
const app = () => {
  return <div className="wrap">Hello, World!</div>;
};

// good
const App = () => {
  return <div className="wrap">Hello, World!</div>;
};

컴포넌트 CSS 스타일링 기본

리액트에서 컴포넌트에 CSS 스타일을 지정하는 방법을 배워봅니다.

인라인 스타일(Inline Style)

인라인 스타일은 HTML 태그의 style 속성을 사용해서 CSS 스타일을 지정하는 방식을 말합니다. 리액트는 자바스크립트를 기본으로 하는 문법이기 때문에 style 속성으로 CSS 속성을 지정할 때 몇 가지 규칙이 있습니다.

  • style 속성에는 객체 형식의 값이 할당되어야 한다.
  • font-size와 같은 케밥 케이스 형식의 속성은 카멜 케이스 형식(fontSize)로 작성해야 한다.
const App = () => {
  return (
    <div>
      <h1
        style={{
          fontSize: "30px",
          color: "#ed4848",
          textDecoration: "line-through",
        }}
      >
        Hello World!
      </h1>
    </div>
  );
};

export default App;

외부 스타일(External Stylesheet)

외부 스타일은 별도의 CSS 파일에 CSS 코드를 작성하고, 리액트 컴포넌트 파일과 연결해서 사용하는 방법을 말합니다.

 

App.tsx

import "./App.css";
const App = () => {
  return (
    <div>
      <h1 className="title">Hello World!</h1>
    </div>
  );
};

export default App;

 

App.css

.title {
  font-size: 30px;
  color: #ed4848;
  text-decoration: line-through;
}

 

CSS Modules ( css 공부를 할 거면 이 방법을 공부하기, 별도의 라이브러리 설치 하지 않고 사용가능합니다.)

모듈 방식을 사용해서 특정 컴포넌트에만 종속되는 CSS 코드를 작성하기 위한 방법입니다. 외부 스타일 방법과 비슷하지만, 파일명이 *.module.css라는 특징이 있습니다.

 

App.moudle.css -> 오타 주의!

.title {
  font-size: 30px;
  color: #ed4848;
  text-decoration: line-through;
}

 

App.tsx

import styles from "./App.module.css";
const App = () => {
  return (
    <div>
      <h1 className={styles.title}>Hello World!</h1>
    </div>
  );
};

export default App;

 

classnames
https://www.npmjs.com/package/classnames

 

classnames

A simple utility for conditionally joining classNames together. Latest version: 2.5.1, last published: 8 months ago. Start using classnames in your project by running `npm i classnames`. There are 43929 other projects in the npm registry using classnames.

www.npmjs.com

import classnames from "classnames";
import classmoudle from "classnames/bind";
import styles from "./App.module.css";
import Second from "./components/Second";
const App = () => {
  const isTrue = false;
  const cx = classmoudle.bind(styles);
  return (
    <>
      <div className={styles.container}>
        <h1 className={classnames("foo", { bar: isTrue })}>App Component</h1>
        <p className={cx("title", { underline: isTrue })}>Sample Text</p>
      </div>
      <Second />
    </>
  );
};
export default App;

Tailwind CSS

대표적인 Atomic CSS 라이브러리.

https://tailwindcss.com/docs/guides/vite

 

Install Tailwind CSS with Vite - Tailwind CSS

Setting up Tailwind CSS in a Vite project.

tailwindcss.com

설치

npm install -D tailwindcss
npx tailwindcss init # tailwind.config.js 생성

 

tailwind.config.js

export default {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

 

src/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

 

vite.config.js

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "tailwindcss";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), vanillaExtractPlugin(), wyw()],
  css: {
    postcss: {
      plugins: [tailwindcss()],
    },
  },
});

 

app.tsx

const App = () => {
  return (
    <div>
      <h1 className="text-[30px] font-bold text-[#ed4848] line-through">
        Hello, World!
      </h1>
    </div>
  );
};

export default App;

+ tailwind css (추가로 공부할 것)

플러그인

tailwind-merge

스타일 충돌 없이 Tailwind CSS 클래스를 JS로 효율적으로 병합하는 유틸리티 함수입니다.

npm: tailwind-merge

 

tailwind-merge

Merge Tailwind CSS classes without style conflicts. Latest version: 2.5.2, last published: 8 days ago. Start using tailwind-merge in your project by running `npm i tailwind-merge`. There are 3395 other projects in the npm registry using tailwind-merge.

www.npmjs.com

npm install tailwind-merge

tailwind-linecamp

말줄임표 처리를 손쉽게 도와주는 플러그인, 단, Tailwind 3.3 이상부터는 기본 내장되어 있어서 별도의 설치가 필요 없습니다.

https://github.com/tailwindlabs/tailwindcss-line-clamp

 

GitHub - tailwindlabs/tailwindcss-line-clamp: A plugin that provides utilities for visually truncating text after a fixed number

A plugin that provides utilities for visually truncating text after a fixed number of lines. - tailwindlabs/tailwindcss-line-clamp

github.com

 

tailwind-forms

테일윈드의 기본 폼 스타일을 추가해주는 패키지입니다.

https://github.com/tailwindlabs/tailwindcss-forms

 

GitHub - tailwindlabs/tailwindcss-forms: A plugin that provides a basic reset for form styles that makes form elements easy to o

A plugin that provides a basic reset for form styles that makes form elements easy to override with utilities. - tailwindlabs/tailwindcss-forms

github.com

 

환경설정

@tailwind 밑줄 없애기

@taiwlind 키워드에 밑줄이 들어오는 건, 해당 키워드가 CSS가 인식하지 못하는 키워드이기 때문입니다. 별도로 밑줄을 없애려고 노력하지 않아도 애플리케이션을 만들어나가는 데 불편한 점은 없지만, 굳이 없애고 싶다면 비주얼 스튜디오의 설정 파일을 수정하면 됩니다.

 

settings.json

"css.lint.unknownAtRules": "ignore", // 추가 or 업데이트

 

📢 테일윈드 공식 홈페이지에서는 "PostCSS Language”를 지원하는 익스텐션을  설치해서 
밑줄을 없애는 것을 제시하고 있습니다. 하지만 본 강사는 익스텐션을 설치하는 것을 굉장히 조심스러워하기 때문에 이 방식보다는 위에서 안내드린 방법으로 해결합니다.

 

settings.json 을 적용하려고 할 때에는 왼쪽 하단의 톱니바퀴 설정을 클릭하고 세팅을 누르고 오른쪽 상단의

가운데 페이지를 클릭하여 위치에 상관없이 복사하여 적용하면 됩니다.

 

모바일에선 hover 스타일 막기

모바일에서는 어차피 hover가 불가능하기 때문에 hover 관련 클래스를 적용되지 않게 설정하면 퍼포먼스 향상을 도모할 수 있습니다.

 

tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  future: {
    hoverOnlyWhenSupported: true,
  },
}

 

😎 react + tailwind + tailwind-merge (스타터 팩) -> 유용하게 사용 가능하므로 사용자 별로 미리 설정해둔 라이브러리 및 세팅 환경들을 깃허브에 올려 놓고 깃 클론하여 사용하게 되면 매번 세팅할 필요 없이 사용가능하기에 편리합니다.!

git clone https://github.com/kisudevel/react-tailwind-stater.git .

 

[스나이퍼 팩토리 리액트 2기 과정]

본 학습 자료 및 코드는 수코딩님의 자료를 이용하였습니다. [수코딩(https://www.sucoding.kr)] 출처

728x90
반응형
LIST