스나이퍼팩토리 사전직무교육 (Day 3)

짜스의 하루 ·2024년 9월 24일

Tailwind CSS

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

Tailwind CSS 공식 홈페이지에서 guildes/vite에 들어가면 vite에 tailwind를 설치할 수 있는 가이드를 제공해 주니 꼭 이걸 보고 따라 설치하도록 한다!

이 과정을 터미널로 따라서 작성하게 되면 Tailwind CSS 설치가 완료된다.

twmerge

Tailwind CSS를 사용할 때 클래스 이름을 쉽게 병합할 수 있도록 도와주는 도구이다.
이 도구는 여러 개의 Tailwind CSS 클래스가 있을 때 중복된 클래스를 제거하고, 최적화된 결과를 생성해주게 된다. 주로 Tailwind CSS로 스타일링할 때, 긴 클래스 문자열을 깔끔하게 정리하는 데 유용하다.

만약,

<div class="bg-red-500 bg-red-500 text-white p-4">
  Hello World
</div>

이와 같이 중복된 속성이 있는 클래스가 있다면,
twmerge를 사용하면 중복된 bg-red-500 클래스를 하나로 줄일 수 있게 된다.
이렇게 하면 CSS의 크기를 줄이고 코드 가독성을 높일 수 있다.

이런식으로 isActive && 'bg-blue-500 border-blue-500'active 한 상태일 때에는 ? css 변경을 할 수 있게 된다.


CSS-in-JS

CSS-in-JS는 JavaScript 내에서 CSS 스타일을 작성하고 관리하는 기법을 의미한다.
이 방법은 전통적인 CSS 파일과는 다르게, 스타일을 JavaScript 코드 내에서 정의하고 컴포넌트와 함께 번들링하는 접근 방식이다.

CSS-in-JS는 컴포넌트 기반 스타일링을 더 쉽게 하고, 스타일의 범위를 컴포넌트로 한정하며, 스타일 충돌을 방지하는 등의 이점을 제공한다. 대표적으로 아래와 같은 CSS-in-JS 라이브러리가 있다.

Styled Components

npm i styled-components 를 터미널에 입력하고 설치하면 된다.

import styled from 'styled-components';

const HelloWorld = styled.h1`
  font-size: 30px;
  color: #ed4848;
  text-decoration: line-through;
  &:hover {
    color: blue;
  }
`;

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

export default App;

styled.h1와 같이 생성할 태그를 .div, .h1 와 같이 입력하고 백틱을 사용해서 적용할 css를 적용하면 된다.


Emotion

아직 인터넷 익스플로러 11이 강성한 사용량을 보이던 시절 인기가 높아진 CSS-in-JS 라이브러리이다.
styled-component는 IE 11을 지원하려면 폴리필(최신 자바스크립트 기능이 오래된 브라우저에서 동작하지 않는 문제를 해결하기 위해 사용되는 코드)을 사용해야 했지만, emotion은 별도의 폴리필 없이도 ie 11을 지원했다.

npm i @emotion/css 로 emotion을 설치하면 사용이 가능해진다.

import { css } from "@emotion/css";

const App = () => {
  return (
    <div>
      <h1
        className={css`
          font-size: 30px;
          color: #ed4848;
          text-decoration: line-through;
          &:hover{
           color:blue;
          }
        `}
      >
        Hello, World!
      </h1>
    </div>
  );
};

export default App;

vanilla extract

제로 런 타임(Zero Run Time)이라는 개념이 새롭게 등장하게 되면서 최근에 급 부상하고 있는 라이브러리라고 한다.

제로 런 타임이란, 런 타임 오버헤드가 없는 CSS를 의미한다.
--> 조금 더 쉽게 말하면 스타일을 적용하는 데 있어서 런타임에 추가적인 자바스크립트 코드 실행이나 연산이 필요하지 않는 CSS를 의미한다.

그렇다면, 런타임 오버헤드란?
런타임 오버헤드란, 프로그램이 실행되는 동안 추가적인 연산이나 자원 소비를 의미한다.
--> 런타임 오버헤드가 발생하면 메모리 사용량이 증가하고, CPU 사용량이 증가하며, 응답 시간 지연이 된다는 성능적 단점이 발생하게 된다.

그럼 제로 런 타임이 아닌 것은 어떤데?
styled-components나 emotion 같은 라이브러리는 ‘컴포넌트가 렌더링 될 때’ 스타일을 생성하여 적용한다.
--> 이러한 라이브러리들은 컴포넌트가 렌더링 될 때마다 스타일을 생성하기 때문에 스타일을 동적으로 생성하거나 조건에 따라 스타일을 변경하는 경우에 유용하지만, 그에 따른 성능 비용이 따르게 된다.

npm i @vanilla-extract/css @vanilla-extract/vite-plugin 를 설치를 하면 된다.

styles.css.ts 폴더에

import { style } from "@vanilla-extract/css";

export const h1Style = style({
  fontSize: "30px",
  color: "#ed3838",
  textDecoration: "line-through",
});

를 적용시키고,

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

export default App;

className={} 에 이렇게 적용시키면 된다.


테일윈드 추가 설정(플러그인)

tailwind-merge

스타일 충돌 없이 Tailwind CSS 클래스를 JS로 효율적으로 병합하는 유틸리티 함수이다.
위에서 twmerge를 살펴보면 된다!

tailwind-linecamp

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

이정도만 알아도 될 것 같다 !


React에 폰트를 적용하는 방법

구글 폰트

구글 폰트를 적용하는 가장 간단한 방법은 @import 문법을 사용해서 CSS 파일에 추가하는 것이다.

이와 같이 Web에서는 <link>, @import로 폰트를 불러올 수 있게 된다.

@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap");

body {
  font-family: "Inter", sans-serif;

<Link>로 추가하는 방법은
index.html 폴더에서 <Link> 태그를 복사해 준 뒤, css 속성을 정의해 주고, 이렇게 사용해주면 적용되는 것을 확인할 수 있다.


눈누 폰트

눈누 링크를 타고 들어가면 다양한 폰트를 확인할 수 있는데, 이 폰트를 사용하기 위해서는 이렇게 @font-face를 font.css에 추가해주면 된다.

그리고 이렇게 정의한 폰트를

.bagel {
  font-family: 'BagelFatOne-Regular';
}

className으로 정의를 해두고, 이렇게 className에 bagel를 적게 되면 적용이 되는 것을 확인할 수 있다!


연습문제

컴포넌트 코드를 각각 header, nav, main, footer의 4개의 컴포넌트를 생성해서 분리해봅시다.

export default function App() {
  return (
    <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>
  );
}

components에 포함될 파일들은 꼭 src/components 폴더 안에 파일들을 위치하도록 해주는 것이 -> 관용이다!

위의 코드를 각각 컨포넌트로 분리하고 이렇게 App() 함수 안에 위치시키면 화면에 렌더링이 되는 것을 확인할 수 있다!


컴포넌트 주요 이벤트 유형

주요 이벤트 유형

폼 이벤트onChange, onSubmit
마우스 이벤트onClick, onMouseEnter, onMouseLeave
키보드 이벤트onKeyDown, onKeyPress, onKeyUp
포커스 이벤트onFocus, onBlur
드래그 이벤트onDragStart, onDrageOver, onDrop
터치 이벤트onTouchStart, onTouchMove, onTouchEnd

마우스 이벤트: onClick, onMouseEnter, onMouseLeave

export default function App() {
  return (
    <>
      <div>App Components</div>
      <button
        onClick={() => {
          alert('클릭');
        }}
        onMouseEnter={() => {
          alert('마우스가 올라갔어요');
        }}
        onMouseDown={() => {
          alert('마우스가 내려갔어요');
        }}
      >
        클릭
      </button>
    </>
  );
}

키보드 이벤트 : onKeyDown, onKeyUp

import React, { useState } from 'react';

const KeyboardExample = () => {
  const [key, setKey] = useState('');

  const handleKeyDown = (event) => {
    setKey(event.key);
  };

  return (
    <div>
      <input type="text" onKeyDown={handleKeyDown} placeholder="Press any key" />
      <p>Last key pressed: {key}</p>
    </div>
  );
};

export default KeyboardExample;

포커스 이벤트: onFocus, onBlur

import React, { useState } from 'react';

const FocusExample = () => {
const [focused, setFocused] = useState(false);

return (

<div>
  <input
    type="text"
    onFocus={() => setFocused(true)}
    onBlur={() => setFocused(false)}
    placeholder="Focus on me!"
  />
  <p>{focused ? 'Input is focused' : 'Input is not focused'}</p>
</div>

);
};

export default FocusExample;


Props

컴포넌트 데이터 전달

리액트에서 ‘props’는 컴포넌트 간에 데이터를 전달하는 방법이다.

‘props’는 부모 컴포넌트가 자식 컴포넌트에 전달하는 읽기 전용 데이터이다.
--> ‘props’는 컴포넌트가 상위 컴포넌트로부터 받는 입력값이라고 할 수 있으며, 컴포넌트는 이를 이용해 렌더링 결과를 동적으로 변경할 수 있다.

<Component 속성={값} />

연습 코드

import React from 'react';

const Greeting = ({ name } : { name : string } ) => {
  return <h1>안녕하세요, {name}님!</h1>;
};

export default Greeting;

이렇게 Greeting 이라는 컴포넌트를 하나 생성했다.

App.tsx

import React from 'react';
import Greeting from './Greeting';

const App = () => {
  const userName = "서연"; // 사용자 이름 정의

  return (
    <div>
      <Greeting name={userName} /> {/* Greeting 컴포넌트에 name props 전달 */}
    </div>
  );
};

export default App;

App.tsx에서 Greeting을 불러온 뒤, name을 넘겨주면?
화면에 안녕하세요, 서연님! 이 뜨게 된다.

또한, name에서 받을 type은 string이기 때문에 string을 지정해주어야 한다.


연습문제 - 프로필 카드 컴포넌트 만들기

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>User Card</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <article class="card">
      <div>
        <img
          class="card-img"
          src="https://images.pexels.com/photos/158251/forest-the-sun-morning-tucholskie-158251.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"
          alt="background-pic"
        />
      </div>
      <div class="profile">
        <img
          class="prof-img"
          src="https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"
          alt="profile-pic"
        />
        <h3 class="alias">Sally Ramos</h3>
        <p class="username">@sallytheramos</p>
        <button>Follow</button>
      </div>
    </article>
  </body>
</html>

문제
다음 주어진 HTML 코드와 CSS 코드가 있습니다. 해당 코드로 만들어지는 UI를 기본으로 하여 ProfileCard 컴포넌트를 만들어주세요. ProfileCard 컴포넌트는 배경 이미지, 유저 이미지, 유저 이름, 인스타 아이디를 Props로 전달 받도록 합니다.

이 문제를 해결하기 위해

ProfileCard 컴포넌트를 새로 생성해 주었다.

interface IProfileCardProps {
  backgroundImage: string;
  profileImage: string;
  userName: string;
  onFollow: string;
}

export default function ProfileCard({
  backgroundImage,
  profileImage,
  userName,
  onFollow,
}: IProfileCardProps) {
  return (
    <>
      <article className="card">
        <div>
          <img
            className="card-img"
            src={backgroundImage}
            alt="background-pic"
          />
        </div>
        <div className="profile">
          <img className="prof-img" src={profileImage} alt="profile-pic" />
          <h3 className="alias">{userName}</h3>
          <p className="username">{onFollow}</p>
          <button>Follow</button>
        </div>
      </article>
    </>
  );
}

여기서 interface로 받아올 것들의 타입을 정해주었고, App.tsx에서 <ProfileCard/>를 불러와서 타입에 맞는 값을 넘겨주면 된다.

본 후기는 본 후기는 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 3기 과정(B-log) 리뷰로 작성 되었습니다.
#유데미 #udemy #웅진씽크빅 #스나이퍼팩토리 #인사이드아웃 #미래내일일경험 #프로젝트캠프 #부트캠프 #React #리액트프로젝트 #프론트엔드개발자양성과정 #개발자교육과정

profile
2024. 01. 02 ~ 백앤드 공부 시작, 2024. 04.01 ~ 프론트 공부 시작

0개의 댓글