νλ‘ νΈμλ κ°λ°μμ λΉ μ§ μ μλ λΆλΆμ΄ μ»΄ν¬λνΈ κ°λ°μΈ κ² κ°μ΅λλ€. κ²μΌλ‘ 보기μλ λ¨μν΄ λ³΄μΌ μ μμ§λ§ μμΈν λ€μ¬λ€λ³΄λ©΄ μ΄λ€ κ°λ°λ³΄λ€λ μ¬μΈνκ³ λ³΅μ‘ν μμ μ΄λΌλ μκ°μ΄ λλλ€. νμ¬ μ λ κ°λ°ν λ μ»΄ν¬λνΈλ₯Ό λκ°μ§ κΈ°μ€μΌλ‘ λλ μ μμ νκ³ μμ΅λλ€. λ¨μ UI λ₯Ό νννλ common μ»΄ν¬λνΈμ UI+κΈ°λ₯ μ λ€λ£¨λ feature μ»΄ν¬λνΈ μ λλ€. ( μ¬κΈ°μ κΈ°λ₯μ κΈ°μ€μ μ»΄ν¬λνΈμμμ APIλ₯Ό νΈμΆνμ¬ λ°μ΄ν°λ₯Ό λ€λ£¨κ±°λ, μνκ΄λ¦¬λ₯Ό ν¨μΌλ‘μ¨ UIμ λ³νλ₯Ό μ£Όλ κ²μ λ§ν©λλ€. )
μ κ° μκ°ν ν©μ± μ»΄ν¬λνΈλ feature μ»΄ν¬λνΈμ μν΄μλ μ»΄ν¬λνΈ μ€ νλμ λλ€. μ΄λ² ν¬μ€ν μμλ ν©μ± μ»΄ν¬λνΈλ 무μμ΄κ³ , ν©μ± μ»΄ν¬λνΈλ₯Ό μ μ©ν κ³Όμ μ λν΄μ μκ°ν΄λ³΄λ €κ³ ν©λλ€.
μ€λ λ€λ£° μ»΄ν¬λνΈλ μ± UIλ₯Ό λνλ΄λ μ»΄ν¬λνΈμ λλ€. (νΈμμ Bookμ»΄ν¬λνΈλΌκ³ λΆλ₯΄κ² μ΅λλ€!) APIλ₯Ό νΈμΆν΄μ μ± μ λν μ 보(μ± μ΄λ―Έμ§, μ± μ΄λ¦, μΆνμ¬ μ΄λ¦, μ± νλκ·Έ)λ₯Ό λ°μμ€κ³ μ΄λ₯Ό UIμ λ§κ² λ°°μΉν΄μ 보μ¬μ€λλ€.
μμ κ°μ Book μ»΄ν¬λνΈλ₯Ό ꡬννκΈ° μν μ½λλ μλμ κ°μ΅λλ€. (μ΄ν΄λ₯Ό λκΈ° μν μμλ‘ μλ΅λ λΆλΆμ΄ μμΌλ μ°Έκ³ ν΄μ λ΄μ£ΌμΈμ!)
import Text from "components/common/text"
import Flag from "components/common/flag"
import OriginalBook from "components/features/original/book"
import OriginalBookImage from "components/features/original/book/image"
import OriginalBook from "components/features/original/book/caption"
import MySchoolBookImage from "components/features/original/book/svg/image"
const BookList = () => {
...
return (
...
{gerOriginalSchoolBookListResponse.data.book_list.map((item, index) => {
return (
<OriginalBook
key={item.id}
onClick={() => onClickBookItem(item.id)}
>
{/* μ±
μ΄λ―Έμ§ png νμΌ */}
{item.thumbnail_url && <OriginalBookImage src={item.thumbnail_url} />}
{/* μ±
μ΄λ―Έμ§ svg νμΌ */}
{item.my_school_thumbnail_detail && (
<MySchoolBookImage schoolName={schoolName} subsubjectName={item.subsubject_name} colorDetail={item.my_school_thumbnail_detail} />
)}
{/* μ±
μΆνμ¬ */}
{item.school_publisher_name && (
<Text type="caption2" color="gray_400">
{item.school_publisher_name}
</Text>
)}
{/* μ±
μ΄λ¦ */}
<OriginalBookCaption>{item.name}</OriginalBookCaption>
{!AuthOriginalActive?.is_active && item.is_trial && <Flag type="book">μ΄ λ¬μ λ¬΄λ£ λ¬Έμ μ§</Flag>}
{/* μ±
νλκ·Έ */}
{!isOpen(item.opened_at) && (
<Flag type="book" color="blue_400">
{getMonth(item.opened_at)} λ§ OPEN μμ
</Flag>
)}
</OriginalBook>
)
})}
...
)
}
μ΄ν΄λ₯Ό λκΈ° μν΄ μ μ½λμ λν΄μ κ°λ¨ν μ€λͺ νμλ©΄
gerOriginalSchoolBookListResponse
λ μ±
μ λν μ 보λ₯Ό κ°κ³ μλ API response κ° μ
λλ€.<OriginalBook />
μ <OriginalImage />
, <OriginalCaption />
λ± μ±
μ ꡬμ±νλλ° νμν μμλ€μ μμμΌλ‘ λ°λ λΆλͺ¨ μ»΄ν¬λνΈμ
λλ€.<OriginalImage />
μ μ±
μ png μ΄λ―Έμ§λ₯Ό λνλ΄λ μ»΄ν¬λνΈ μ
λλ€.<OriginalCaption />
μ μ±
μ μ΄λ¦μ λνλ΄λ μ»΄ν¬λνΈ μ
λλ€.<MySchoolBookImage />
μ μ±
μ svg μ΄λ―Έμ§λ₯Ό λλλ§ ν΄μ£Όλ μ»΄ν¬λνΈ μ
λλ€.<Flag />
λ μ±
μ νλκ·Έ(9μ λ§ μ€νμμ λ±)λ₯Ό λνλ΄λ κ³΅μ© μ»΄ν¬λνΈ μ
λλ€.<Text />
λ μΆνμ¬ μ΄λ¦μ λνλ΄λ κ³΅μ© μ»΄ν¬λνΈ μ
λλ€.μμ κ°μ λ°©μμΌλ‘ μ»΄ν¬λνΈλ₯Ό ꡬμ±νμ λ μ κ° λκΌλ λ¬Έμ μ μ μλμ κ°μ΅λλ€.
<OriginalBook>, <OriginalBookImage>, <OriginalBookCaption>
μ κ°κ° import ν΄μΌν©λλ€.<OriginalBookCaption>
μ μ¬μ© κΈ°μ€μ΄ κ°λ°μλ§λ€ λ€λ¦
λλ€.<OriginalBookCaption>
propsλ‘ μ΄λ¦μ λ°λκ² μλλΌ childrenμΌλ‘ λ°κ³ μμ΄μ λꡬλ μ±
μ΄λ¦, μ±
μΆνμ¬κΉμ§ ν¬ν¨νκ³ , λꡬλ μ±
μ΄λ¦κΉμ§λ§ ν¬ν¨μν€κ³ μ±
μΆνμ¬λ λ°λ‘ μΆκ°νλ λ°©μμΌλ‘ μ¬μ©λμ΄ νμ
μ νΌλμ μ£Όκ³ μμ΅λλ€.<OriginalBookImage>
μ νμ¬ png νμΌλ§ λ°κ³ μμ΄μ svg νμΌμ κ²½μ° κ°κ° νμ΄μ§λ§λ€ ν΄λΉ μ»΄ν¬λνΈ <MySchoolBookImage>
λ₯Ό import ν΄μ μΆκ°ν΄μΌ ν©λλ€. <Flag>
μ»΄ν¬λνΈλ₯Ό import ν΄μ μΆκ°ν΄μΌ ν©λλ€.μμ κ°μ μ μ λ°μμν€κΈ° μν λ°©λ²μ μ°Ύμ보λ€κ° ν©μ± μ»΄ν¬λνΈμ λν΄μ μκ² λμκ³ μ κ° νλ €κ³ νλ λ°©ν₯μ±κ³Ό λ§λ€κ³ νλ¨λμ΄ μ μ©νκ² λμμ΅λλ€. λ¨Όμ ν©μ± μ»΄ν¬λνΈμ λν΄μ κ°λ¨ν μκ°νλλ‘ νκ² μ΅λλ€.
ν©μ± μ»΄ν¬λνΈλ νλμ μ»΄ν¬λνΈλ₯Ό μ¬λ¬ κ°μ§ μ§ν©μ²΄λ‘ λΆλ¦¬ν λ€, λΆλ¦¬λ κ° μ»΄ν¬λνΈλ₯Ό μ¬μ©νλ μͺ½μμ μ‘°ν©ν΄μ μ¬μ©νλ μ»΄ν¬λνΈ ν¨ν΄μ μλ―Έν©λλ€.
κ°λ¨ν μμλ‘ htmlμ selectλ₯Ό λ³Ό μ μλλ°, selectλ <select>
μ <option>
νκ·Έμ μ‘°ν©μΌλ‘ μ΄λ£¨μ΄μ§λλ€. <select>
μ <option>
μ κ°κ° λ
립μ μΌλ‘λ ν° μλ―Έκ° μμ§λ§ μ¬μ©νλ κ³³μμ μ΄λ₯Ό μ‘°ν©ν΄ μ¬μ©ν¨μΌλ‘μ¨ νλ©΄μ μλ―Έ μλ μμκ° λ©λλ€.
<select>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
ν©μ± μ»΄ν¬λνΈ ν¨ν΄μ μ μ©ν μμλ₯Ό μν΄ κ°κ°μ μ»΄ν¬λνΈλ€μ μμΈν props μ λ‘μ§λ€μ ν¬ν¨λμ΄ μμ§ μμΌλ μ΄ μ μ°Έκ³ ν΄μ λ΄μ£ΌμΈμ!
htmlμ <option>
νκ·Έμ ν΄λΉνλ μλΈ μ»΄ν¬λνΈλ₯Ό ꡬνν©λλ€. Book μ»΄ν¬λνΈλ₯Ό ꡬμ±νλ κ°κ°μ μ»΄ν¬λνΈλ€μ΄ μ¬κΈ°μ ν΄λΉν©λλ€. <BookImage> <BookCaption> <BookFlag>
htmlμ <select>
νκ·Έμ ν΄λΉνλ λ©μΈ μ»΄ν¬λνΈλ₯Ό ꡬνν©λλ€. μλΈ μ»΄ν¬λνΈλ€μ λ¬Άμ΄μ νλ©΄μ μ μνκ² λ³΄μ΄λλ‘ νλ Wrapper μ±κ²©μ μ»΄ν¬λνΈμ
λλ€.
import styled from "@emotion/styled"
import {ClickMotion} from "@repo/ui"
export interface OriginalBookGroupProps extends OriginalBookContextValue {
children: React.ReactNode
onClick?: () => void
}
export type Props = OriginalBookGroupProps & HTMLAttributes<HTMLDivElement>
export const OriginalBookGroup: React.FC<Props> = ({children, onClick, ...props}) => {
return (
<ClickMotion onClick={onClick}>
<Wrapper {...props}>
{children}
</Wrapper>
</ClickMotion>
)
}
const Wrapper = styled.div<{width?: string}>`
position: relative;
display: flex;
flex-direction: column;
gap: 8px;
`
μμ²λΌ μμ±νλ©΄ childrenμΌλ‘ λ€μ΄μ€λ μλΈ μ»΄ν¬λνΈλ€μ λ°μ μ μκ³ μμλλ‘ λ°°μΉ ν μ μμ΅λλ€.
μ΄λ κ² κ΅¬νλ μ»΄ν¬λνΈλ€μ λ¬Άμ΄μ exportνλ©΄ κ°κ°μ μ»΄ν¬λνΈκ° Bookμ μλΈ μ»΄ν¬λνΈμμ μ’ λ νμ€νκ² ν μ μμ΄ μ½λμ κ°λ μ±μ λμμ΄ λ μ μμ΅λλ€.
import {OriginalBookGroup} from "./group"
import {OriginalBookImage} from "./image"
import {OriginalBookCaption} from "./caption"
import {OriginalBookFlag} from "./flag"
export const OriginalBook = Object.assign(OriginalBookGroup, {
Image: OriginalBookImage,
Caption: OriginalBookCaption,
Flag: OriginalBookFlag
})
import { OriginalBook } from "components/features/original/book"
const BookList = () => {
...
return (
...
{gerOriginalSchoolBookListResponse.data.book_list.map((item, index) => (
<OriginalBook key={item.id} onClick={() => onClickBookItem(item.id)}>
<OriginalBook.Flag isTrial={item.is_trial} isLms={item.is_lms_exclusive} openedAt={item.opened_at} />
<OriginalBook.Image
pngUrl={item.thumbnail_url}
svgColorInfo={item.my_school_thumbnail_detail}
subsubjectName={item.subsubject_name}
/>
<OriginalBook.Caption bookName={item.name} publisherName={item.school_publisher_name} />
</OriginalBook>
))}
...
)
}
μμ κ°μ μ μ€μ νλμΈ κ°λ°μλ€μ΄ μ»΄ν¬λνΈλ₯Ό μ¬μ©ν λ νΌλμ μ€μ΄κΈ° μν΄μλ μ»΄ν¬λνΈμ λν λ¬Έμμλ£κ° νμνμ΅λλ€. μ»΄ν¬λνΈλ₯Ό λ¬Έμννλ λꡬμλ μ€ν 리λΆκ³Ό κ°μ λΌμ΄λΈλ¬λ¦¬λ€μ΄ μ‘΄μ¬νμ§λ§ λ°λ‘ λμ νκΈ°μλ λ¬λ컀λΈκ° μλ€λ κ²μ κ³ λ €νμ¬ μΌλ¨ νΌκ·Έλ§μ μ 리ν΄μ νμλ€μκ² κ³΅μ νμ΅λλ€.
μ»΄ν¬λνΈ κ°μ ν μ΄μ λ³΄λ€ κ°λ¨ν΄μ§ μ½λλ₯Ό 보면μ λΏλ―ν¨μ λκΌλ€. μ΄ μ μ΄ λ¦¬ν©ν λ§νλ ν° μ΄μ μ€ νλμΈ κ² κ°λ€. μ²μ ν©μ± μ»΄ν¬λνΈ κ°λ μ λν΄μ μ νμ λλ μ μ΄ν΄κ° κ°μ§ μμλλ° λ΄κ° μ€μ λ‘ μ»΄ν¬λνΈμ μ μ©ν λͺ©μ μΌλ‘ κ³μ λ³΄λ€ λ³΄λ μ΄ν΄νκΈ°κ° μμνλ κ² κ°λ€. μμ λλ μ€μ λ‘ λ΄κ° λΆλͺνλ΄μΌ μλ κ² κ°λ€!γ γ
μ»΄ν¬λνΈ κ°λ°μ νμ μ²μ μμν λλ μ΄ μ λλ κΈλ°© νκ² μ§? λΌλ λ§μμΌλ‘ μμνλλ° νλ€ λ³΄λ©΄ μ΄λ €μ΄ μ μ΄ μ겨μ κΈΈκ² κ³ λ―Όνκ² λλ κ² κ°λ€. κ·Όλ° λλ μ΄λ° κ³ λ―Όνλ μκ°μ΄ λμμ§λ§μ μμ κ² κ°λ€. μ΄λ° κ³ λ―Όμ ν΅ν΄μ μ»΄ν¬λνΈκ° λ¨μν κΈ°λ₯μ΄λ UI λ¨μλ‘ μͺΌκ°λκΈ°λ§ ν κ²μ΄ μλλΌ μ§μ§ ν¨μ¨μ μΈ κ°λ°μ μν μμ€ λμ μ»΄ν¬λνΈκ° λ μ μλ κ³Όμ μ΄λΌλ μκ°μ΄ λ€κΈ° λλ¬Έμ΄λ€. κ·Έλ¦¬κ³ λ΄κ° κ°λ°ν μ»΄ν¬λνΈλ₯Ό λ€λ₯Έ κ°λ°μλ€μ΄ νΈνκ² μ°κ³ μΌκ΄λ μ½λμ UIλ₯Ό 보면 κ·Έλλ§νΌ λ λΏλ―ν κ²μλ κ²κ°λ€!γ γ γ
μ΄λ² μ»΄ν¬λνΈ κ°μ μ νλ©΄μ μμ¬μ λ μ μ λ¬Έμν λΆλΆμ΄λ€. μμ μ μ€ν 리λΆμ λμ νλ €κ³ νμλλ° κ·Έ λΉμ λ€λ₯Έ κΈ°λ₯ κ°λ°μ μ°μ μμκ° λ°λ €μ μ λλ‘ λμ νμ§ λͺ»νμλ€. μ΄μ μ»΄ν¬λνΈκ° μ μ λ§μμ§λ©΄μ λ¬Έμνμ νμμ±μ λν΄μ ν¬κ² λλΌκ³ μλ€. ν루빨리 μ€ν 리λΆμ μ λλ‘ λμ ν΄μ μ λλ‘ λ λμμΈμμ€ν μ ꡬμΆνκ³ μΆλ€!