※ 사전 세팅
깃허브 레포로 가서 실습에 쓸 파일만 가져오기 (ch03 > ch03_2 폴더)
https://github.com/yisj777s/doit-react-webapp-typescript/tree/main
터미널에서 아래 코드를 쳐서 그 아래에 있는 캡처(package.json)에 들어간 패키지들 일괄 설치
npm i -D

템플릿 파일 살짝 수정 - src./copy/CopyMe.tsx
import { Title } from "../components";
export default function CopyMe() {
return (
<section className="className">
<Title>CopyMe</Title>
<div className="mt-4"></div>
</section>
)
}
일단 템플릿 파일들을 전부 복사해서 pages 안에 넣어준다.
cd src
cp copy/CopyMe.tsx pages/DivTest.tsx
cp copy/CopyMe.tsx pages/ViewportTest.tsx
cp copy/CopyMe.tsx pages/HeightTest.tsx
cp copy/CopyMe.tsx pages/PaddingTest.tsx
cp copy/CopyMe.tsx pages/MarginTest.tsx
cp copy/CopyMe.tsx pages/ImageTest.tsx
cp copy/CopyMe.tsx pages/BackgroundImageTest.tsx
cp copy/CopyMe.tsx pages/DisplayTest.tsx
cp copy/CopyMe.tsx pages/AvatarTest.tsx
cp copy/CopyMe.tsx pages/PositionTest.tsx
cp copy/CopyMe.tsx pages/OverlayTest.tsx
App쪽에 가서도 미리 판만 깔아두기 - src/App.tsx
import DivTest from './pages/DivTest'
import ViewportTest from './pages/ViewportTest'
import HeightTest from './pages/HeightTest'
import PaddingTest from './pages/PaddingTest'
import MarginTest from './pages/MarginTest'
import ImageTest from './pages/ImageTest'
import BackgroundImageTest from './pages/BackgroundImageTest'
import DisplayTest from './pages/DisplayTest'
import DisplayNoneTest from './pages/DisplayNoneTest'
import AvatarTest from './pages/AvatarTest'
import PositionTest from './pages/PositionTest'
import OverlayTest from './pages/OverlayTest'
export default function App() {
return (
<main>
<OverlayTest />
<PositionTest />
<DisplayNoneTest />
<DisplayTest />
<AvatarTest />
<BackgroundImageTest />
<ImageTest />
<MarginTest />
<PaddingTest />
<HeightTest />
<ViewportTest />
<DivTest />
</main>
)
}
(※ 여기선 몇 가지만 실습하는 걸로...)
Div
아래 경로로 2개의 파일 생성
touch ./src/components/WidthHeight.ts
touch ./src/components/Div.tsx
Div에 쓰일 WidthHeight 타입 구현
src/components/WidthHeight.ts
export type WidthHeight = {
width?: string
height?: string
}
WidthHeight 타입을 적용해서 Div 컴포넌트 생성
src/components/Div.tsx
import type {FC, DetailedHTMLProps, HTMLAttributes, PropsWithChildren} from 'react'
import type {WidthHeight} from './WidthHeight'
import type {LeftRightTopBottom} from './LeftRightTopBottom'
export type ReactDivProps = DetailedHTMLProps<
HTMLAttributes<HTMLDivElement>,
HTMLDivElement
>
export type DivProps = ReactDivProps &
PropsWithChildren<WidthHeight> &
LeftRightTopBottom & {
src?: string
}
//prettier-ignore
export const Div: FC<DivProps> = ({
width, height, style: _style, src, className: _className,
left, right, top, bottom, ...props
}) => {
const style = {
..._style, width, height, backgroundImage: src && `url(${src})`,
left, right, top, bottom
}
const className = ['box-border', src && 'bg-gray-300', _className].join(' ')
return <div {...props} className={className} style={style} />
}
Div 컴포넌트 반영 (컴파일에 쓰기 위해 넘겨주기)
src/components/index.ts
export * from './Icon'
export * from './Texts'
export * from './Div'
Div 컴포넌트 사용
src/pages/DivTest.tsx
import {Title, Subtitle, Div, Icon} from '../components'
export default function DivTest() {
return (
<section className="mt-4">
<Title>DivTest</Title>
{/* <Div className="text-center text-blue-100 bg-blue-600" height="6rem"> */}
<Div className="text-center text-blue-100 bg-blue-600">
<Icon name="home" className="text-3xl"></Icon>
<Subtitle>Home</Subtitle>
</Div>
</section>
)
}

Viewport
src/pages/ViewportTest.tsx
import { Title } from "../components";
export default function ViewportTest() {
return (
<section className="w-screen h-screen mt-4 bg-indigo-900">
<Title className="text-white">ViewportTest</Title>
</section>
)
}

BackgroundImage
먼저 Div 쪽에 이미지 출처를 받아오는 src 부분을 넣어줘야 한다.
src/components/Div.tsx

src/pages/BackgroundImageTest.tsx
import { Div, Title, Subtitle } from '../components'
import * as D from '../data'
const src = D.randomImage(1200, 400);
export default function BackgroundImage () {
return (
<section className="mt-4">
<Title>BackgroundImageTest</Title>
<Div className="mt-4 bg-gray-300 h-80" src={src}>
<Subtitle className='text-gray-500'>Some Text here</Subtitle>
</Div>
</section>
)
}

Avatar
src/components 경로에 파일 생성
touch src/components/Avatar.tsx
src/components/Avatar.tsx
import type { FC } from 'react';
import { Div } from './Div';
import type { DivProps } from './Div';
export type AvatarProps = DivProps & {
size?: string;
}
export const Avatar: FC<AvatarProps> = ({
className: _className, style, src, size, ...props
}) => {
const w_or_h = size ?? '3rem';
const className = ['rounded-full bg-cover bg-gray-300', _className].join(' ');
return (
<Div
{...props}
src={src}
width={w_or_h}
height={w_or_h}
className={className}
style={style}
/>
)
}
추가
src/components/index.ts

기능 함수 구현
src/pages/AvatarTest.tsx
import { Div, Title, Avatar } from '../components'
import * as D from '../data'
export default function AvatarTest () {
const avatars = D.range(0, 10).map(index => (
<Avatar
className="inline-block -ml-6 border-4 border-white"
key={index}
src={D.randomAvatar()}
/>
))
return (
<section className="mt-4">
<Title>AvatarTest</Title>
<Div className="px-12 py-4 m-8 bg-blue-300">{avatars}</Div>
</section>
)
}

Position
그 전에 상우하좌 기능 구현
src/components/LeftRightTopBottom.ts
export type LeftRightTopBottom = {
left?: string;
right?: string;
top?: string;
bottom?: string;
}
Div 쪽에 속성 추가
src/components/Div.tsx

Position 사용하기
src/pages/PositionTest.tsx
import {Div, Title, Icon} from '../components'
import * as D from '../data'
const src = D.randomImage(800, 500)
//prettier-ignore
export default function PositionTest() {
const icons = ['home', 'search', 'settings', 'favorite'].map(name => (
<Icon key={name} name={name} className="mr-2" />
))
return (
<Div>
<Title>PositionTest</Title>
<Div className="relative border-2 border-gray-500"
src={src} height="10rem">
<Div className="absolute p-2 text-white bg-red-500"
left="1rem" top="1rem">{icons}</Div>
<Div className="absolute p-2 text-white bg-blue-500"
right="1rem" top="1rem">{icons}</Div>
<Div className="absolute p-2 text-white bg-pink-500"
left="1rem" bottom="1rem">{icons}</Div>
<Div className="absolute p-2 text-white bg-yellow-500"
right="1rem" bottom="1rem">{icons}</Div>
</Div>
</Div>
)
}

Overlay
Overlay 컴포넌트 파일 생성
touch src/components/Overlay.tsx
src/components/Overlay.tsx
import {FC} from 'react'
import {ReactDivProps} from './Div'
import {Div} from './Div'
export type OverlayProps = ReactDivProps & {
opacityClass?: string
}
export const Overlay: FC<OverlayProps> = ({
className: _className,
opacityClass,
children,
...props
}) => {
const className = [
_className,
'absolute z-50 w-screen h-screen',
opacityClass ?? 'bg-black/70',
'flex items-center justify-center'
].join(' ')
//prettier-ignore
return (
<Div {...props} className={className} top="0" left="0">{children}</Div>
)
}
추가
src/components/index.ts

src/pages/OverlayTest.tsx
import {Title, Div, Icon, Overlay} from '../components'
export default function OverlayTest() {
return (
<section className="mt-4">
<Title>OverlayTest</Title>
<Overlay opacityClass="bg-black/70">
<Div className="relative flex items-center justify-center p-8 bg-white h-1/2">
<Div className="absolute" right="1rem" top="1rem">
<Icon name="close" className="text-gray-500" />
</Div>
<p className="text-5xl">modal dialog box</p>
</Div>
</Overlay>
</section>
)
}

'Front-End > React' 카테고리의 다른 글
| React #11 (CSS - user & card) (0) | 2025.07.14 |
|---|---|
| React #10 (CSS - flexbox layout) (0) | 2025.07.14 |
| React #8 (CSS - TailwindCSS) (0) | 2025.07.10 |
| React #7 (CSS - 아이콘) (2) | 2025.07.09 |
| React #6 (CSS - bootstrap) (1) | 2025.07.09 |