컴포넌트 CSS 스타일링
- 인라인 스타일
- HTML의 style 속성을 이용해 스타일을 지정하는 방법
- style 속성은 객체 형식으로 전달되어야 함
- camel-case로 작성
<h1
style={{
width: "100%",
backgroundColor: "red",
}}
>
Hello World
</h1>;
- 외부 css 파일 사용
- css 파일을 import하여 className으로 스타일 지정
- 외부 css 파일을 import한 컴포넌트가 렌더링 되어있기만 하면, 모든 컴포넌트는 해당 컴포넌트에서 import한 css의 영향을 받음 → global하게 적용
- ex) App.tsx 아래에 Second.tsx와 Third.tsx라는 컴포넌트가 존재
- Second.tsx에 style.css가 import 되어있어도, Second.tsx가 렌더링 되어있다면 App.tsx에서도 스타일 사용 가능
- 그렇기 때문에, 최상위 컴포넌트 (Main.tsx)에 import 하는 것이 best
/* style.css */
.title {
width: 100%;
background-color: red;
}
//App.tsx
import styles from "./style.css";
<h1 className="title">
Hello World
</h1>
- 모듈 CSS
- 모듈 방식을 사용해서 특정 컴포넌트에만 종속되는 스타일 작성
- 외부 스타일 방법과 비슷하지만, 파일명이 *.module.css
- import 한 컴포넌트 이외의 컴포넌트에서는 스타일 사용 불가
/* style.module.css */
.title {
width: 100%;
background-color: red;
}
//App.tsx
import styles from "./style.module.css";
<h1 className={styles.title}>Hello World!</h1>
Hello World
</h1>
- Tailwind CSS
- React와 Typescript 사용시 가장 핫한 라이브러리
- 가독성이 매우 떨어지고 코드가 복잡해지는 단점
- NEXT.JS에서 Tailwind를 권장
Tailwind css
tailwind-merge → 중복 스타일 제거 및 클래스 코드 병합
import { twMerge } from "tailwind-merge";
export default function App() {
return (
<h1 className={twMerge("text-rose-500 font-bold", "text-black")}>
Hello world!
</h1>
);
}
//className에 바로 지정하면 class="text-rose-500 font-bold", "text-black"
//twMerge를 사용하면 class="font-bold", "text-black"로 출력
//중복 스타일은 뒤쪽의 스타일을 적용
twMerge를 사용하면 조건부 스타일링도 가능
import { twMerge } from "tailwind-merge";
export default function App() {
let isActive = true;
return (
<button
className={twMerge(
"text-rose-500",
isActive && "text-white font-bold"
)}
>
Hello world!
</button>
);
}
tailwind의 css 속성이 아닌 클래스 역시 twMerge 내에 넣으면 병합 가능
import { twMerge } from "tailwind-merge";
export default function App() { return (
<button
className={twMerge(
"isActive",
"text-rose-500 text-white font-bold"
)}
>
Hello world!
</button>
);
}
클래스명의 일부를 바꾸는 것은 허용되지 않음
import { twMerge } from "tailwind-merge";
export default function App() {
let color = "rose";
return (
<button
className={twMerge(
// 문자열 일부를 변수로 치환하는 것은 허용하지 않음
"text-${color}-500 font-bold"
)}
>
Hello world!
</button>
);
}
export default function App() {
let isActive = true;
return (
<button
className={twMerge(
// 문자열 전체를 치환하는 것은 가능
`${isActive ? "text-rose-500" : "text-blue-500"}`,
"font-bold"
)}
>
Hello world!
</button>
);
}
CSS 우선순위
important > 인라인 스타일 > id > 클래스 > 태그
구글 폰트 적용 방법
- 구글 폰트에서 폰트 검색 후 Get Font
- import문을 css 파일에 추가 or link 태그를 index.html에 추가
- css 파일에 클래스 선택자 생성
- 클래스명을 이용해 폰트 적용
다운로드 폰트 적용 방법
@font-face {
font-family: "fontName"; /* 폰트 이름 정의 */
src: url("./fonts/EF_jejudoldam\\(OTF\\).woff2"),
url("./fonts/EF_jejudoldam_OTF_.woff"); /* 폰트파일 위치 및 포맷 지정 */
}
컴포넌트 렌더링
React는 DFS를 바탕으로 컴포넌트를 탐색
→ 최상위 컴포넌트로부터 렌더링 시작 → 가장 먼저 렌더링되는 컴포넌트의 자식 컴포넌트들을 모두 렌더링→ 그 다음 컴포넌트를 렌더링
export default function App() {
return;
<>
<A>
<E />
<F />
</A>
<B>
<G />
</B>
<C>
<H />
</C>
<D></D>
</>;
}
위 구조에서, 렌더링 순서는
A → E → F → B → G → C → H → D
컴포넌트 이벤트
컴포넌트에 이벤트 props를 전달하여 이벤트 추가 가능
//인라인 이벤트 -> 컴포넌트에 직접 이벤트 코드 작성
function App() {
return (
<button
onClick={() => {
alert("클릭");
}}
>
클릭
</button>
);
}
export default App;
//이벤트 함수 할당 -> 이벤트 함수 작성 후 컴포넌트에 할당
function App() {
const alerClick = () => {
alert("클릭");
};
return <button onClick={alerClick}>클릭</button>;
}
export default App;
이벤트 타입
React 이벤트도 타입을 명시해 주어야 타입스크립트 오류가 나지 않음 → 이벤트에 마우스를 올리면 툴팁으로 표시
React.적용하려는 이벤트<적용하려는 태그>
ex) 클릭 이벤트 ⇒ React.MouseEvent<HTMLDivElement>
function App() {
const submit = (e: React.FormEvent<HTMLFormElement>) => {
alert("제출!");
};
return (
<>
<form onSubmit={submit}>
<input type="text" name="email"></input>
<input type="password" name="password"></input>
<button>로그인</button>
</form>
</>
);
}
export default App;
👏 form 태그에는 submit을 넣어주면 매우 편리
이벤트 전파
이벤트는 부모 컴포넌트에서 자식 컴포넌트로 전달 가능 → 반대는 불가
컴포넌트 props
컴포넌트에서 컴포넌트로 전달하는 값 → 객체 형태로 전달
//App.tsx
import Count from "./components/count";
function App() {
return (
<div>
<h1>title</h1>
<Count count={10} operator="+"></Count>
</div>
);
}
export default App;
//Count.tsx
//props는 객체 형태로 전달되므로, 객체의 타입을 지정해줘야 함
interface Props {
count: number;
operator: string;
}
function Count(props: Props) {
return (
<div>
{props.count} {props.operator}
</div>
);
}
export default Count;
//비구조화 할당 가능
interface Props {
count: number;
operator: string;
}
function Count({ count, operator }: Props) {
return (
<div>
{count} {operator}
</div>
);
}
export default Count;
props의 타입
props는 객체이기 때문에, interface 등으로 객체의 타입을 지정해 주는 것이 중요
→ 파일명.d.ts 파일에 interface를 저장하면 import 없이 인터페이스 사용 가능
⇒ 타입스크립트에서 자체적으로 d.ts 파일을 인식하여 전역으로 타입 관리
//count.d.ts
//Props를 export 하지 않아도 사용 가능
interface Props {
count: number;
operator: string;
}
//Subcount.tsx
//Props를 import 하지 않아도 사용 가능
function SubCount({ count, operator }: Props) {
return (
<div>
{count} {operator}
</div>
);
}
export default SubCount;
함수 props 전달
함수 역시 타입 추론을 통해 interface에 타입을 지정하고, prop로 전달 가능
//App.tsx
import Count from "./components/Count";
function App() {
const calculate = (count: number, operator: string): number => {
switch (operator) {
case "+":
return count + count;
case "-":
return count - count;
case "*":
return count * count;
case "/":
return count / count;
}
return 0;
};
return (
<div>
<h1>title</h1>
<Count count={10} operator="+" calculate={calculate}></Count>
</div>
);
}
export default App;
//Count.tsx
function Count({ count, operator, calculate }: Props) {
return <div>{calculate(count, operator)}</div>;
}
export default Count;
//count.d.ts
interface Props {
count: number;
operator: string;
calculate: (count: number, operator: string) => number;
}
매개변수의 유무에 따라 함수 전달 방식이 바뀜
//매개변수가 있을 때
export default function App() {
const printText = (str: string) => {
alert(str);
};
return (
<>
<h1>App Coponent</h1>
<button onClick={() => printText('클릭')}>클릭</button>
</>
);
}
//매개변수가 없을 때
export default function App() {
const printText = () => {
alert("클릭");
};
return (
<>
<h1>App Coponent</h1>
<button onClick={printText}>클릭</button>
</>
);
}
children
태그 내에 존재하는 요소 = Content
컴포넌트의 Content를 props로 전달하기 위해서는 ‘children’으로 전달
children의 타입은 React.ReactNode로 고정
children은 props와 다르게 HTML 태그까지 전달 가능 + 컴포넌트도 children으로 전달 가능
//App.tsx
import Button from "./components/html/Button";
export default function App() {
const printText = (str: string) => {
alert(str);
};
return (
<>
<div className="item-middle">
//"클릭" 텍스트만 전달
<Button children="클릭"></Button>
//em(기울임) 태그가 적용된 텍스트 전달
<Button children={<em>엔터</em>}></Button>
</div>
</>
);
}
//Button.tsx
function Button({ children }: { children: React.ReactNode }) {
return (
<>
<button className="py-2 px-4 rounded-lg text-sm border border-gray-500">
{children}
</button>
</>
);
}
export default Button;
본 게시글의 예제 코드는 수코딩(https://www.sucoding.kr)을 참고했습니다
'Frontend Study' 카테고리의 다른 글
[유데미x스나이퍼팩토리] Next.js 3기 - 사전직무교육 5일차 (1) | 2024.09.27 |
---|---|
[유데미x스나이퍼팩토리] Next.js 3기 - 사전직무교육 4일차 (0) | 2024.09.26 |
[유데미x스나이퍼팩토리] Next.js 3기 - 사전직무교육 2일차 (0) | 2024.09.24 |
[유데미x스나이퍼팩토리] Next.js 3기 - 사전직무교육 1일차 (0) | 2024.09.23 |
[FE_Bootcamp] Main Project_로그인/회원가입 (0) | 2023.08.10 |