※ 이전 프로젝트 (ch03_4) 에서 계속 이어서 진행.
https://chiro-j.tistory.com/74
React #10 (CSS - flexbox layout)
※ 사전 작업더보기깃허브 레포로 가서 실습에 쓸 파일만 가져오기 (ch03 > ch03_3 폴더) https://github.com/yisj777s/doit-react-webapp-typescript/tree/main터미널에서 아래 코드를 쳐서 그 아래에 있는 캡처(package.j
chiro-j.tistory.com
User Container
사용자 데이터 만들기
touch src/data/User.ts
사용자 타입 정의 (+ 사용자 생성 메서드 정의)
src/data/User.ts
import * as C from './chance'
import * as I from './image'
export type IUser = {
uuid: string;
name: string;
jobTitle: string;
email: string;
avatar: string;
}
// prettier-ignore
export const makeUser = (
uuid: string, name: string, jobTitle: string, email: string, avatar: string
): IUser => ({uuid, name, jobTitle, email, avatar})
export const makeRandomUser = (): IUser =>
makeUser(
C.randomUUID(),
C.randomName(),
C.randomJobTitle(),
C.randomEmail(),
I.randomAvatar()
)
위에서 정의한 사용자 데이터를 반영
src/data/index.ts

User 컴포넌트 구현
src/pages/User.tsx
import type { FC } from 'react'
import type { DivProps } from '../components'
import * as D from '../data'
import { Div, Avatar } from '../components'
export type UserProps = DivProps & {
user: D.IUser
}
const User: FC<UserProps> = ({user, ...props}) => {
const {name, email, jobTitle, avatar} = user;
return (
<Div {...props}>
<div className="flex p-2">
<Avatar src={avatar} size="2rem" />
<div className="ml-2">
<p className="font-blod">{name}</p>
<p className="text-gray-500 line-clamp-1">{jobTitle}</p>
<p className="text-blue-500 underline">{email}</p>
</div>
</div>
</Div>
)
}
export default User;
위에서 만든 사용자 데이터와 사용자 컴포넌트를 이용하여 페이지에 구현하기
src/pages/UserContainer.tsx
import {Title} from '../components'
import * as D from '../data'
import User from './User'
export default function UserContainer() {
const children = D.makeArray(10)
.map(D.makeRandomUser)
.map(user => (
<User
key={user.uuid}
user={user}
className="m-2 text-xs border-2 border-blue-300 rounded-lg"
minWidth="15rem"
width="15rem"
/>
))
return (
<section className="mt-4">
<Title>UserContainer</Title>
<div className="flex flex-wrap items-center justify-center p-4 mt-4">
{children}
</div>
</section>
)
}
(기본적으로 flex-box 모델로 되어 있어서, 마우스 휠로 확대/축소 함에 따라 다르게 보여진다.)

Card Container
더 나아가 위의 User Container 기반으로 그 위에 이미지를 얹어 Photo Card Container로 만들어보자
카드 데이터 만들기
touch src/data/Card.ts
카드 타입 정의 (+ 카드 만드는 메서드 정의)
src/data/Card.ts
import type {IUser} from './User'
import {makeRandomUser} from './User'
import * as C from './chance'
import * as I from './image'
import * as D from './date'
export type ICard = {
uuid: string
writer: IUser
image: string
title: string
paragraphs: string
dayMonthYearDate: string
relativeDate: string | null
}
export const makeCard = (
uuid: string,
writer: IUser,
image: string,
title: string,
paragraphs: string,
dayMonthYearDate: string,
relativeDate: string | null
): ICard => ({uuid, writer, image, title, paragraphs, dayMonthYearDate, relativeDate})
export const makeRandomCard = () => {
const date = D.makeRandomPastDate()
return makeCard(
C.randomUUID(),
makeRandomUser(),
I.randomImage(800, 600),
C.randomTitleText(),
C.randomParagraphs(5),
D.makeDayMonthYear(date),
D.makeRelativeDate(date)
)
}
위에서 정의한 데이터를 반영하기
src/data/index.ts

위에서 앞서 작업했으니 이제 페이지에 구현해보자
src/pages/Card.tsx
import type {FC} from 'react'
import type {DivProps} from '../components'
import {Div, Icon} from '../components'
import * as D from '../data'
import User from './User'
export type CardProps = DivProps & {
card: D.ICard
}
const Card: FC<CardProps> = ({card, ...props}) => {
console.log(card)
const {writer, image, title, paragraphs, dayMonthYearDate, relativeDate} = card
const icons = ['home', 'search', 'settings', 'favorite'].map(name => (
<Icon key={name} name={name} className="mr-20 text-3xl" />
))
return (
<Div {...props}>
<div className="flex flex-col">
<Div src={image} className="h-60" />
<Div className="p-4" minHeight="16rem" height="16rem" maxHeight="16rem">
<p className="mt-2 text-3xl font-bold text-center">{title}</p>
<Div className="flex justify-between">
<User user={writer} className="mt-2" />
<Div className="mt-2">
<p className="text-gray-500">{relativeDate}</p>
<p className="text-gray-500">{dayMonthYearDate}</p>
</Div>
</Div>
<p className="mt-2 line-clamp-4">{paragraphs}</p>
<Div className="flex flex-row items-center justify-between p-2 mt-2 text-red-500">
{icons}
</Div>
</Div>
</div>
</Div>
)
}
export default Card;
(이것도 마찬가지로 화면 해상도에 따라 달리 보여진다.)

'Front-End > React' 카테고리의 다른 글
| React #12-2 (CSS - daisyUI: Input & Modal) (1) | 2025.07.14 |
|---|---|
| React #12-1 (CSS - daisyUI: Button & Icon) (0) | 2025.07.14 |
| React #10 (CSS - flexbox layout) (0) | 2025.07.14 |
| React #9 (CSS - 박스모델) (2) | 2025.07.10 |
| React #8 (CSS - TailwindCSS) (0) | 2025.07.10 |