2023. 12. 7. 14:55γDevelopmentπ©π»π¦³/Static Web
https://github.com/mijoungkim/Travel-Website-React.js-Next.js-13-Tailwind-CSS
μ°μ ν°λ―Έλμ npx create-next-app@latestλ₯Ό ν΅ν΄ μ’ μμ±μ μ€μΉν΄μ€λλ€
μ¬μ©μκ° μλ‘μ΄ Next.js νλ‘μ νΈλ₯Ό μμ±ν λ,
λ€μν μ΅μ μ μ νν μ μκ² νκΈ° μν΄ λνλ©λλ€.
κ° νλͺ©μ μλ―Έλ λ€μκ³Ό κ°μ΅λλ€:
TypeScript μ¬μ© μ¬λΆ: νλ‘μ νΈμμ TypeScriptλ₯Ό μ¬μ©ν κ²μΈμ§ 묻λ μ§λ¬Έμ
λλ€.
TypeScriptλ JavaScriptμ μ μ νμ
μ μΆκ°νμ¬ λ κ²¬κ³ νκ³ μμ μ μΈ μ½λλ₯Ό μμ±ν μ μκ² λμμ€λλ€.
ESLint μ¬μ© μ¬λΆ: μ½λ νμ§κ³Ό μΌκ΄μ±μ μ μ§νκΈ° μν΄ ESLintλ₯Ό μ¬μ©ν κ²μΈμ§ 묻λ μ§λ¬Έμ
λλ€. ESLintλ μ½λλ₯Ό λΆμνμ¬ λ¬Έμ λ₯Ό μ°Ύκ³ κ³ μΉ μ μλλ‘ λμμ£Όλ λꡬμ
λλ€.
Tailwind CSS μ¬μ© μ¬λΆ: μ νΈλ¦¬ν°-νΌμ€νΈ CSS νλ μμν¬μΈ Tailwind CSSλ₯Ό νλ‘μ νΈμ μ μ©ν κ²μΈμ§ 묻λ μ§λ¬Έμ
λλ€. Tailwind CSSλ₯Ό μ¬μ©νλ©΄ λ°μν λμμΈμ λΉ λ₯΄κ² ꡬνν μ μμ΅λλ€.
src/ λλ ν 리 μ¬μ© μ¬λΆ: νλ‘μ νΈμ μμ€ νμΌλ€μ src/ λλ ν 리 μμ ꡬ쑰ννμ¬ κ΄λ¦¬ν κ²μΈμ§ 묻λ μ§λ¬Έμ
λλ€. μ΄ κ΅¬μ‘°λ₯Ό μ¬μ©νλ©΄ νλ‘μ νΈλ₯Ό λ κΉλνκ² κ΄λ¦¬ν μ μμ΅λλ€.
App Router μ¬μ© μ¬λΆ: Next.js 13λΆν° λμ
λ App Routerλ₯Ό μ¬μ©ν κ²μΈμ§ 묻λ μ§λ¬Έμ
λλ€. App Routerλ₯Ό μ¬μ©νλ©΄ νμ΄μ§ λΌμ°ν
λ° λ°μ΄ν° λ‘λ©μ λ μ μΈμ μΌλ‘ κ΄λ¦¬ν μ μμ΅λλ€.
κΈ°λ³Έ import alias (@/*) 컀μ€ν
μ¬λΆ: νλ‘μ νΈ λ΄μμ μ¬μ©ν κΈ°λ³Έ import κ²½λ‘ λ³μΉμ μ¬μ©μ μ μν κ²μΈμ§ 묻λ μ§λ¬Έμ
λλ€. μ΄λ₯Ό μ¬μ©νλ©΄ νλ‘μ νΈ λ΄μ κ²½λ‘λ₯Ό λ μ½κ² κ΄λ¦¬ν μ μμ΅λλ€.
μ μ€μ λ€μ νλ‘μ νΈμ μ΄κΈ° μ€μ λ¨κ³μμ μ¬μ©μμ κ°λ° νκ²½κ³Ό μ νΈμ λ§κ² νλ‘μ νΈλ₯Ό ꡬμ±νλ λ° λμμ μ€λλ€.
Next.js-?
μ μ μ¬μ΄νΈ μμ±(SSG)κ³Ό μλ² μ¬μ΄λ λ λλ§(SSR)μ λͺ¨λ μ§μνλ React κΈ°λ° νλ μμν¬λ‘, μ΄λ₯Ό ν΅ν΄ λΉ λ₯΄κ³ μ΅μ νλ μΉμ¬μ΄νΈλ₯Ό ꡬμΆν μ μμ΅λλ€. μ μ μ¬μ΄νΈ μμ±μ μ¬μ΄νΈμ κ° νμ΄μ§λ₯Ό λΉλ νμμ 미리 μμ±νμ¬ λ°°ν¬νλ λ°©μμΌλ‘, μ΄λ λ‘λ© μκ°μ μ€μ΄κ³ μ±λ₯μ ν₯μμν΅λλ€.
Next.jsμ μν
μ΄κΈ° νμ΄μ§ λ‘λ μ μ¬μ©μμκ² λ³΄μ¬μ€ HTMLκ³Ό CSSλ₯Ό 미리 μμ±ν©λλ€.
λ°μ΄ν° νμΉ(Data Fetching) - μλ₯Ό λ€μ΄, API νΈμΆμ μλ²μμ μννμ¬ νμ΄μ§μ νμν λ°μ΄ν°λ₯Ό μ¬μ μ μ€λΉν©λλ€.
SEO μ΅μ ν - κ²μ μμ§μ΄ μ½ν
μΈ λ₯Ό μΈλ±μ±νκΈ°μ μ ν©ν μμ±λ νμ΄μ§λ₯Ό μ 곡ν©λλ€.
리μ‘νΈμ Node.jsμ μ°¨μ΄μ μ-?
Reactλ ν΄λΌμ΄μΈνΈ μ¬μ΄λ λΌμ΄λΈλ¬λ¦¬λ‘, μ¬μ©μ μΈν°νμ΄μ€λ₯Ό λ§λλ λ° μ¬μ©λ©λλ€.
Next.jsλ React κΈ°λ°μ μλ² μ¬μ΄λ λ λλ§μ μ§μνλ νλ μμν¬λ‘,
μλ² λ λλ§ λ° μ μ μ¬μ΄νΈ μμ± λ±μ μΆκ° κΈ°λ₯μ μ 곡ν©λλ€.
λ λͺ
λ Ήμ΄ λͺ¨λ npxλ₯Ό μ¬μ©νμ¬ μ€νλλ©°, npxλ Node.jsλ₯Ό μ€μΉν λ ν¨κ» μ€μΉλλ λꡬμ
λλ€.
μ΄λ₯Ό ν΅ν΄ Node ν¨ν€μ§λ₯Ό λ‘컬 μ€μΉ μμ΄ μ€νν μ μμ΅λλ€.
ν°λ―Έλ λͺ λ Ήμ΄
npm run davλ‘ ννμ΄μ§ λμ΄ν μ΄κΈ° ui λ¨Όμ νμΈν΄μ£ΌμΈμ
μ΄κΈ° μΈν ν pageμμλ κΈ°λ³Έ μ½λλ€μ μ λΆ μ§μμ€ν
ν μ€νΈμ½λ λ¨Όμ μμ±ν΄λ΄ λλ€
μ μμ μΌλ‘ μΆλ ₯λλ€λ©΄ μ΄λ tailwindμ΄ μμλνκ³ μλ€λκ²λλ€ !
globals.css
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Inter;
}
@layer utilities {
.btn_white {
@apply border-white bg-white px-8 py-3 text-green-50
}
.btn_white_text {
@apply border-white bg-white px-8 py-3 text-gray-90
}
.btn_green {
@apply border-green-50 bg-green-50 px-8 py-5 text-white
}
.btn_dark_green {
@apply bg-green-90 px-8 py-4 text-white transition-all hover:bg-black
}
.btn_dark_green_outline {
@apply border-gray-20 bg-green-90 px-8 py-5 text-white
}
.max-container {
@apply mx-auto max-w-[1440px];
}
.padding-container {
@apply px-6 lg:px-20 3xl:px-0;
}
.flexCenter {
@apply flex items-center justify-center;
}
.flexBetween {
@apply flex items-center justify-between;
}
.flexStart {
@apply flex items-center justify-start;
}
.flexEnd {
@apply flex items-center justify-end;
}
/* FONTS */
.regular-64 {
@apply text-[64px] font-[400] leading-[120%];
}
.regular-40 {
@apply text-[40px] font-[400] leading-[120%];
}
.regular-32 {
@apply text-[32px] font-[400];
}
.regular-24 {
@apply text-[24px] font-[400];
}
.regular-20 {
@apply text-[20px] font-[400];
}
.regular-18 {
@apply text-[18px] font-[400];
}
.regular-16 {
@apply text-[16px] font-[400];
}
.regular-14 {
@apply text-[14px] font-[400];
}
.medium-14 {
@apply text-[14px] font-[600];
}
.bold-88 {
@apply text-[88px] font-[700] leading-[120%];
}
.bold-64 {
@apply text-[64px] font-[700] leading-[120%];
}
.bold-52 {
@apply text-[52px] font-[700] leading-[120%];
}
.bold-40 {
@apply text-[40px] font-[700] leading-[120%];
}
.bold-32 {
@apply text-[32px] font-[700] leading-[120%];
}
.bold-20 {
@apply text-[20px] font-[700];
}
.bold-18 {
@apply text-[18px] font-[700];
}
.bold-16 {
@apply text-[16px] font-[700];
}
/* Hero */
.hero-map {
@apply absolute right-0 top-0 h-screen w-screen bg-pattern-2 bg-cover bg-center md:-right-28 xl:-top-60;
}
/* Camp */
.camp-quote {
@apply absolute -right-6 bottom-4 w-[140px] lg:bottom-10 xl:-right-8 xl:w-[186px] 3xl:right-0;
}
/* Feature */
.feature-phone {
@apply absolute top-[13%] z-10 hidden max-w-[1500px] rotate-[15deg] md:-left-16 lg:flex 3xl:left-20;
}
/* Get App */
.get-app {
@apply max-container relative flex w-full flex-col justify-between gap-32 overflow-hidden bg-green-90 bg-pattern bg-cover bg-center bg-no-repeat px-6 py-12 text-white sm:flex-row sm:gap-12 sm:py-24 lg:px-20 xl:max-h-[598px] 2xl:rounded-5xl;
}
}
/* Hide scrollbar for Chrome, Safari and Opera */
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
.hide-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
μ΄ μ½λλ Tailwind CSSλ₯Ό μ¬μ©νλ νλ‘μ νΈμ μ μ μ€νμΌ μνΈμ λλ€.
Tailwind CSSλ μ νΈλ¦¬ν° μ°μ μ CSS νλ μμν¬λ‘,
λ€μν λμμΈ μμλ₯Ό ν΄λμ€λ‘ λΉ λ₯΄κ² μ μ©ν μ μκ² ν΄μ€λλ€.
μ½λμ λ΄μ©μ λ€μκ³Ό κ°μ΅λλ€:
Google Fonts μ°κ²°:
Google Fontsμ 'Inter' ν°νΈ ν¨λ°λ¦¬λ₯Ό μν¬νΈν©λλ€. μ΄ ν°νΈλ μΉμ¬μ΄νΈμμ μ¬μ©λ κΈ°λ³Έ ν°νΈλ‘ μ€μ λ©λλ€.
Tailwind CSSμ ν΅μ¬ μ€νμΌ μ μ©: @tailwind base;, @tailwind components;, @tailwind utilities;
μ§μμ΄λ₯Ό ν΅ν΄ Tailwind CSSμ κΈ°λ³Έ μ€νμΌ, μ»΄ν¬λνΈ μ€νμΌ, μ νΈλ¦¬ν° μ€νμΌμ μ μ©ν©λλ€.
κΈλ‘λ² μ€νμΌ μ΄κΈ°ν: λͺ¨λ μμμ λν΄ λ§μ§κ³Ό ν¨λ©μ 0μΌλ‘ μ€μ νκ³ ,
box-sizingμ border-boxλ‘ μ€μ ν©λλ€.
Body μ€νμΌ μ€μ : μΉμ¬μ΄νΈμ λ³Έλ¬Έμ 'Inter' ν°νΈλ₯Ό μ μ©ν©λλ€.
μ νΈλ¦¬ν° ν΄λμ€ μ μ: @layer utilities λΈλ‘ μμμ λ€μν 컀μ€ν
μ νΈλ¦¬ν° ν΄λμ€λ₯Ό μ μν©λλ€.
μ΄ ν΄λμ€λ€μ λ²νΌ, 컨ν
μ΄λ, flex λ μ΄μμ, ν°νΈ μ€νμΌ λ± μΉμ¬μ΄νΈμμ μ¬μ¬μ©λ μ€νμΌμ μ μ©ν©λλ€.
μ€ν¬λ‘€λ° μ¨κΈ°κΈ°: .
hide-scrollbar ν΄λμ€λ₯Ό μ¬μ©νμ¬ μΉν· κΈ°λ° λΈλΌμ°μ
(Chrome, Safari, Opera), IE, Edge, Firefoxμμ μ€ν¬λ‘€λ°λ₯Ό μ¨κΉλλ€.
μ΄ μ½λλ μ 체μ μΌλ‘ μΉμ¬μ΄νΈμ μ€νμΌλ§μ μν κΈ°λ³Έ μ€μ μ μ 곡νλ©°,
Tailwind CSSμ ν΄λμ€λ₯Ό μ¬μ©νμ¬ μΌκ΄λ λμμΈ μμ€ν μ ꡬμΆν©λλ€.
μ¬μ©μ μ μ μ νΈλ¦¬ν° ν΄λμ€λ₯Ό ν΅ν΄ λ°λ³΅μ μΌλ‘ μ¬μ©λλ μ€νμΌ ν¨ν΄μ μ½κ² μ μ©ν μ μμΌλ©°,
μ μ§ κ΄λ¦¬μ νμ₯μ±μ ν₯μμν¬ μ μμ΅λλ€.
ν΄λΉ νλ‘μ νΈκ° λλλ©΄ νμ΄μμ λν΄μλ νλ² λ€λ€λ³΄λ μκ°μ κ°μ Έλ³΄λλ‘ νκ² μ΅λλ€
<main className="relative overflow-hidden">
{children}
<main> νκ·Έ: HTMLμμ <main> νκ·Έλ μΉ νμ΄μ§μ μ£Όμ μ½ν
μΈ λ₯Ό λνλ
λλ€. μ΄ νκ·Έ μμ λ€μ΄κ°λ λ΄μ©μ νμ΄μ§μ ν΅μ¬μ μΈ λ΄μ©μ ν¬ν¨νλ©°, λ¬Έμμ μ£Όλ νλ¦μ μνλ μμλ€μ΄ μμΉν©λλ€.
className μμ±: className="relative overflow-hidden"μ CSS ν΄λμ€λ₯Ό μ§μ ν©λλ€. μ΄ ν΄λμ€λ€μ μ€νμΌλ§μ μν΄ μ¬μ©λ©λλ€. relativeλ ν΄λΉ μμμ μμΉλ₯Ό μλμ μΌλ‘ μ€μ νκ³ , overflow-hiddenμ λ΄μ©μ΄ μμμ κ²½κ³λ₯Ό λμ΄κ° λ 보μ΄μ§ μλλ‘ μ²λ¦¬ν©λλ€.
{children}: {children}μ Reactμμ μ¬μ©λλ νΉλ³ν propμ
λλ€.
μ΄λ₯Ό ν΅ν΄ λ€λ₯Έ μ»΄ν¬λνΈμμ λκ²¨μ§ μμ μμλ€μ μ΄ μμΉμ λ λλ§ν μ μμ΅λλ€.
μλ₯Ό λ€μ΄, RootLayout μ»΄ν¬λνΈ μμμ <RootLayout><p>Hello World</p></RootLayout>μ²λΌ μ¬μ©νλ©΄, <p>Hello World</p>κ° <main> νκ·Έ μμμ λ λλ§λ©λλ€.
μ΄ κ΅¬μ‘°λ₯Ό μ¬μ©ν¨μΌλ‘μ¨, RootLayout μ»΄ν¬λνΈλ₯Ό μ¬μ¬μ©ν λ λ€μν λ΄μ©μ <main> νκ·Έ μμ μ μ°νκ² μ½μ
ν μ μμ΅λλ€. μ΄λ μ»΄ν¬λνΈ κΈ°λ° μν€ν
μ²μμ λ§€μ° μΌλ°μ μΈ ν¨ν΄μΌλ‘, μ½λμ μ¬μ¬μ©μ±κ³Ό μ μ§λ³΄μμ±μ λμ¬μ€λλ€.
import React from 'react' μλ΅κ°λ₯ -?
import React from 'react' λ¬Έμ₯μ React 17 λ²μ μ΄μ μμ JSXλ₯Ό μ¬μ©νλ μ»΄ν¬λνΈλ₯Ό μμ±ν λ νμνμ΅λλ€. JSXλ JavaScript XMLμ μ½μλ‘, React μμλ₯Ό μμ±νλ λ° μ¬μ©λλ ꡬ문μ
λλ€. React 17 λ²μ μ΄μ μμλ, JSXλ₯Ό μ¬μ©νλ λͺ¨λ νμΌμμ Reactλ₯Ό μν¬νΈν΄μΌ νμΌλ©°, μ΄λ JSX μ½λκ° React.createElement νΈμΆλ‘ λ³νλκΈ° λλ¬Έμ
λλ€.
κ·Έλ¬λ React 17 λ²μ λΆν°λ μλ‘μ΄ JSX λ³νμ λμ
νμ¬ import React from 'react' λ¬Έμ₯μ΄ λ μ΄μ νμνμ§ μκ² λμμ΅λλ€. μ΄ μλ‘μ΄ λ³νμ JSXλ₯Ό μ²λ¦¬ν λ Reactλ₯Ό λ²μ μμ ν¬ν¨νμ§ μμλ λλλ‘ ν΄μ€λλ€. λ°λΌμ React 17 μ΄μμ μ¬μ©νλ€λ©΄ import React from 'react' λ¬Έμ₯μ μλ΅ν μ μμ΅λλ€.
κ·Έλ¬λ μ¬μ ν λͺ κ°μ§ μ£Όμν΄μΌ ν μ¬νμ΄ μμ΅λλ€:
React λ²μ : νλ‘μ νΈκ° React 17 μ΄μμ μ¬μ©νκ³ μλμ§ νμΈν΄μΌ ν©λλ€.
ꡬ λ²μ μμλ μ¬μ ν μ΄ μν¬νΈκ° νμν©λλ€.
λ¦°ν
λꡬ: μΌλΆ λ¦°ν
λꡬλ μ½λ νΈμ§κΈ°λ μ¬μ ν import Reactκ° νμνλ€κ³ κ²½κ³ ν μ μμ΅λλ€.
μ΄λ λꡬμ μ€μ μ μ λ°μ΄νΈν΄μΌ ν μλ μμμ μλ―Έν©λλ€.
(νμ§λ§ μ΄λ° μ€μ μμ κΉμ§ μμμ΅λλ€.)
μ μ μΉ μ¬μ΄νΈ: μ μ μΉ μ¬μ΄νΈλ₯Ό λ§λ€ λλ Reactλ₯Ό μ¬μ©νλ κ²½μ°,
React λ²μ μ λ°λΌ μ΄ μν¬νΈμ νμμ±μ΄ κ²°μ λ©λλ€.
λ¨μν HTML, CSS, JavaScriptλ§μ μ¬μ©νμ¬ μ μ μΉ μ¬μ΄νΈλ₯Ό λ§λ€ κ²½μ° React μμ²΄κ° νμ μμ΅λλ€.
μμ½νμλ©΄, React 17 μ΄μμ μ¬μ©νλ κ²½μ°μλ import React from 'react'λ₯Ό μλ΅ν μ μμΌλ, νλ‘μ νΈμ React λ²μ κ³Ό λꡬ μ€μ μ νμΈν΄μΌ ν©λλ€.
μ΄κΈ° ꡬν νλ©΄μ λλ€
<ul> νκ·Έλ₯Ό μ΄μ©
hidden lg:flex ν΄λμ€λ₯Ό μ¬μ©νμ¬ λν νλ©΄μμλ§ νμλλλ‘ μ€μ λμ΄ μμ΅λλ€.
hidden ν΄λμ€λ λͺ¨λ νλ©΄ ν¬κΈ°μμ μμλ₯Ό μ¨κΈ°κ³ ,
lg:flex ν΄λμ€λ large breakpoint μ΄μμ νλ©΄ ν¬κΈ°μμλ§ flex 컨ν
μ΄λλ‘ λ§λλλ€.
gap-12 ν΄λμ€λ flex μμ΄ν
μ¬μ΄μ κ³ μ λ κ°κ²©μ μ€μ ν©λλ€.
h-full ν΄λμ€λ <ul> μμμ λμ΄λ₯Ό λΆλͺ¨μ λμ΄μ λμΌνκ² μ€μ ν©λλ€.
κ° Link μ»΄ν¬λνΈλ κ³ μ ν key propλ₯Ό κ°μ§κ³ μμ΅λλ€.
μ΄λ Reactκ° λ¦¬μ€νΈ μμ΄ν
μ ν¨μ¨μ μΌλ‘ λ λλ§νκ³ μ¬μ¬μ©νκΈ° μν κ²μ
λλ€.
flexCenter ν΄λμ€λ μ¬μ©μ μ§μ ν΄λμ€μ¬μ©,
μ΄λ ν΄λΉ μμλ₯Ό flex 컨ν
μ΄λλ‘ λ§λ€κ³ μμ΄ν
λ€μ μ€μμ μ λ ¬ν΄μ€λλ€
regular-16κ³Ό text-gray-50μ κ°κ° κΈκΌ΄ ν¬κΈ°μ μμμ μ€μ νλ ν΄λμ€μ
λλ€
pb-1.5λ ν¨λ© λ°ν
(padding-bottom)μ μ€μ ν©λλ€.
transition-all ν΄λμ€λ λͺ¨λ μμ± λ³κ²½μ λν΄ μ ν ν¨κ³Όλ₯Ό μ μ©ν©λλ€.
hover:font-boldμ μ¬μ©μκ° λ§ν¬ μλ‘ λ§μ°μ€λ₯Ό μ¬λ Έμ λ κΈκΌ΄μ κ΅΅κ² λ§λλλ€.
className={``}μ className={' '} μ¬μ΄μλ μ£Όλ‘ λ κ°μ§ μ£Όμ μ°¨μ΄μ
κ°μ νμ
κ³Ό λ΄μ©:
className={``}: μ΄κ²μ JavaScript ννμμΌλ‘ ν΄μλ©λλ€. μ¬κΈ°μ μ¬μ©λ λ°±ν±(``)μ ES6μ ν
νλ¦Ώ 리ν°λ΄μ λνλ
λλ€. μ΄ κ²½μ° ν
νλ¦Ώ 리ν°λ΄μ λΉ λ¬Έμμ΄μ λνλ
λλ€. λ°λΌμ, μ΄ ννμ κ³΅λ°±μ΄ μλ λΉ λ¬Έμμ΄μ classNameμ ν λΉν©λλ€.
className={' '}: μ΄κ²λ JavaScript ννμμ΄μ§λ§, μ¬κΈ°μλ μμ λ°μ΄ν('')λ₯Ό μ¬μ©νμ¬ λ¬Έμμ΄μ λνλ
λλ€.
μ΄ λ¬Έμμ΄μ 곡백 λ¬Έμ νλλ₯Ό ν¬ν¨ν©λλ€. λ°λΌμ,
μ΄ ννμ 곡백 λ¬Έμκ° ν¬ν¨λ λ¬Έμμ΄μ classNameμ ν λΉν©λλ€.
μ€μ HTML ν΄λμ€ ν λΉμ λ―ΈμΉλ μν₯:
className={``}: μ΄ ννμ μ€μ λ‘ HTML μμμ μλ¬΄λ° ν΄λμ€λ₯Ό μΆκ°νμ§ μμ΅λλ€.
λΉ λ¬Έμμ΄μ΄κΈ° λλ¬Έμ, ν΄λμ€ μμ±μ λΉμ΄ μκ² λ©λλ€.
className={' '}: μ΄ ννμ 곡백 λ¬Έμλ₯Ό ν¬ν¨νλ ν΄λμ€λ₯Ό μΆκ°ν©λλ€. HTMLμμ ν΄λμ€ μ΄λ¦μ 곡백μΌλ‘ ꡬλΆλλ―λ‘, μ΄ κ³΅λ°±μ μ€μ λ‘λ 무μλ μ μμ§λ§, κΈ°μ μ μΌλ‘λ 곡백 λ¬Έμλ₯Ό ν¬ν¨νλ ν΄λμ€λ₯Ό μΆκ°νλ €κ³ μλν©λλ€.
μμ½νμλ©΄, className={``}λ λΉ ν΄λμ€λ₯Ό, className={' '}λ 곡백 λ¬Έμλ₯Ό ν¬ν¨νλ ν΄λμ€λ₯Ό ν λΉν©λλ€.
κ·Έλ¬λ μ€μ HTML λ λλ§μμλ λ κ²½μ° λͺ¨λ ν΄λμ€κ° μλ κ²μ²λΌ λ³΄μΌ μ μμ΅λλ€.
μμ©
첫 λ²μ§Έ μ½λ (className={flexCenter gap-3 rounded-full border ${variant} ${full && 'w-full'}}):
μ΄ μ½λλ ν νλ¦Ώ 리ν°λ΄μ μ¬μ©ν©λλ€. ν νλ¦Ώ 리ν°λ΄μ λ°±ν±(``)μΌλ‘ λλ¬μΈμΈ λ¬Έμμ΄λ‘,
λ¬Έμμ΄ λ΄μμ ${} ꡬ문μ μ¬μ©ν΄ JavaScript ννμμ μ½μ
ν μ μμ΅λλ€.
μ¬κΈ°μ ${variant}μ ${full && 'w-full'}λ λ³μ variantμ κ°κ³Ό 쑰건문 full && 'w-full'μ κ²°κ³Όλ₯Ό ν΄λΉ μμΉμ λ¬Έμμ΄λ‘ μ½μ
ν©λλ€.
κ²°κ³Όμ μΌλ‘, variant λ³μμ κ°κ³Ό full λ³μκ° trueμΌ κ²½μ° 'w-full' ν΄λμ€κ° μ€μ ν΄λμ€ λ¬Έμμ΄μ λμ μΌλ‘ ν¬ν¨λ©λλ€.
λ λ²μ§Έ μ½λ (className={'flexCenter gap-3 rounded-full border ${variant} ${full && 'w-full'}}`):
μ΄ μ½λλ μΌλ° λ¬Έμμ΄μ μ¬μ©ν©λλ€. μμ λ°μ΄ν('')λ‘ λλ¬μΈμΈ μ΄ λ¬Έμμ΄μ λ¬Έμ κ·Έλλ‘ ν΄μλ©λλ€.
${variant}μ ${full && 'w-full'}μ μ΄ κ²½μ° λ¨μν λ¬Έμμ΄λ‘ μ·¨κΈλλ©°,
JavaScript ννμμΌλ‘μμ κΈ°λ₯μ νμ§ μμ΅λλ€.
κ²°κ³Όμ μΌλ‘, λ¬Έμμ΄μ λ³μλ 쑰건문μ κ°μ λ°μνμ§ μκ³ , 'flexCenter gap-3 rounded-full border ${variant} ${full && 'w-full'}'λΌλ μ νν λ¬Έμμ΄μ classNameμ ν λΉν©λλ€.
μμ½νλ©΄, 첫 λ²μ§Έ μ½λλ λ³μμ 쑰건문μ κ°μ λμ μΌλ‘ ν΄λμ€ μ΄λ¦μ ν¬ν¨μν€λ λ°λ©΄,
λ λ²μ§Έ μ½λλ λ³μμ 쑰건문μ λ¬Έμμ΄μ μΌλΆλ‘μ μ μ μΌλ‘ μ¬μ©ν©λλ€.
μ΄ μ°¨μ΄λ ν΄λμ€ μ΄λ¦μ΄ μ€μ λ‘ μ΄λ»κ² ꡬμ±λλμ§μ μν₯μ λ―ΈμΉ©λλ€.
첫 λ²μ§Έ μ½λλ λ μ μ°νκ³ λμ μΈ ν΄λμ€ ν λΉμ κ°λ₯νκ² ν©λλ€.
μμλ₯Ό λ€μλ©΄
className={'flexCenter gap-3 rounded-full border ' + (variant || '') + ' ' + (full ? 'w-full' : '')}
μ΄λ°μμΌλ‘ λ°μ΄νλ₯Ό μ¬μ©νμ¬ μ½λλ₯Ό μ§λ λμ§λ§ μ½λ 볡μ‘μ±κ³Ό κ°λ μ±μ΄ λ¨λ¬μ§λ€λ λ¨μ μ΄μλ€
className={`flexCenter gap-3 rounded-full border ${variant || ''} ${full ? 'w-full' : ''}`}
ν νλ¦Ώ 리ν°λ΄ μ¬μ©μ΄ μ½λκ° κ°κ²°ν΄μ§λ€λ³΄λ νμ¬μλ 리ν°λ΄μ μ¬μ©νμ¬ λ¦¬μ‘νΈ μ½λλ₯Ό λ§μ΄μ§κ³€νλλ°
μ§λ κ³Όμ μμ μ°λ¦¬λ€ μ€μ€λ‘κ° λ¦¬ν©ν°λ§μ ν΄λκ°λκ²μ΄λ€
리ν°λ΄ μ¬μ©μ μ₯μ
κ°λ
μ±: ν
νλ¦Ώ 리ν°λ΄μ μ½λμ κ°λ
μ±μ ν₯μμν΅λλ€. λ³μμ ννμμ΄ λͺ
ννκ² λ³΄μ¬μ§λ©°, λ¬Έμμ΄ λ΄μμ μ§μ μ½μ
λ©λλ€. μ΄λ‘ μΈν΄ μ½λλ₯Ό μ½κ³ μ΄ν΄νκΈ°κ° λ μ¬μμ§λλ€.
κ°κ²°μ±: ν
νλ¦Ώ 리ν°λ΄μ + μ°μ°μλ₯Ό μ¬μ©νμ¬ μ¬λ¬ λ¬Έμμ΄μ μ°κ²°νλ κ²λ³΄λ€ ν¨μ¬ κ°κ²°ν©λλ€.
μ½λκ° λ μ§§κ³ λͺ
λ£ν΄μ§λ©°, μ€λ₯ λ°μ κ°λ₯μ±λ μ€μ΄λλλ€.
μ μ°μ±: ν
νλ¦Ώ 리ν°λ΄μ 볡μ‘ν λ¬Έμμ΄ μ‘°ν©λ μ½κ² μ²λ¦¬ν μ μκ² ν΄μ€λλ€.
λ©ν°λΌμΈ λ¬Έμμ΄, ννμ μ½μ
, λ³μ ν¬ν¨ λ±μ΄ λ κ°λ¨ν΄μ§λλ€.
μ μ§λ³΄μ: ν
νλ¦Ώ 리ν°λ΄μ μ¬μ©νλ©΄ μ½λμ μ μ§λ³΄μκ° λ μ¬μμ§λλ€. μ½λμ λͺ
νμ±κ³Ό κ°κ²°μ±μ΄ λμμ§κΈ° λλ¬Έμ, λμ€μ μ½λλ₯Ό μμ νκ±°λ νμ₯ν λ μ€λ₯λ₯Ό μ€μΌ μ μμ΅λλ€.
λ°λΌμ, ν
νλ¦Ώ 리ν°λ΄μ νλ JavaScriptμ React κ°λ°μμ λ¬Έμμ΄ μ‘°μμ μμ΄ μ νΈλλ λ°©μμ
λλ€. μ΄λ νΉν 볡μ‘ν λ¬Έμμ΄ μ‘°μμ΄λ λμ ν΄λμ€ μ΄λ¦ μμ±κ³Ό κ°μ κ²½μ°μ λμ± κ·Έλ μ΅λλ€.
λ¬Έμμ΄ λ΄μμ λ³μλ ννμμ μ¬μ©ν μ μλ κΈ°λ₯μ κ°λ¦¬ν€λ
ν κΈμλ‘ μ μνλ μ©μ΄λ 보κ°λ²(Interpolation)μ λλ€.
ν νλ¦Ώ 리ν°λ΄ λ΄μμ μ¬μ©λλ ${} ꡬ문μ ν΅ν΄ μ΄λ£¨μ΄μ§λ©°,
μλ°μ€ν¬λ¦½νΈμμλ μ΄λ₯Ό ν΅ν΄ λ¬Έμμ΄ μμ λ³μμ κ°μ μ½μ νκ±°λ ννμμ κ³μ°ν μ μμ΅λλ€.
ν νλ¦Ώ 리ν°λ΄ μ¬μ© (보κ°λ² μ¬μ©):
`... ${expression} ...`
μ΄ κ΅¬λ¬Έμ ννμμ νκ°νμ¬ λ¬Έμμ΄μ ν΄λΉ λΆλΆμ κ²°κ³Όλ₯Ό μ½μ ν©λλ€.
μμλ°μ΄ν/ν°λ°μ΄ν μ¬μ© (보κ°λ² λ―Έμ¬μ©):
'... ${expression} ...'
λλ
"... ${expression} ..."
μ΄ κ΅¬λ¬Έλ€μ ννμμ λ¬Έμ κ·Έλλ‘μ κ°μΌλ‘ μ²λ¦¬νλ©°, ννμμ νκ°νμ§ μκ³ λ¨μν λ¬Έμμ΄μ μΌλΆλ‘ ν¬ν¨ν©λλ€.
μμ½νλ©΄, ν
νλ¦Ώ 리ν°λ΄κ³Ό 보κ°λ²μ μ¬μ©νλ©΄ λμ μΈ κ°μ λ¬Έμμ΄μ ν¬ν¨μν¬ μ μκ³ ,
μμλ°μ΄νλ ν°λ°μ΄νλ₯Ό μ¬μ©νλ©΄ μ μ μΈ λ¬Έμμ΄λ§μ μ²λ¦¬ν μ μλ€λ μ μ΄ μ£Όλ μ°¨μ΄μ λλ€.
νμΌκ΅¬μ‘°μ μΈλΆν(μ‘°μ§ν)
κ°λ μ±: κ° μ»΄ν¬λνΈκ° μμ μ νμΌμ κ°μ§λ―λ‘ μ½λλ₯Ό μ°Ύκ³ μ΄ν΄νκΈ° μ½μ΅λλ€.
μ΄λ μ μ§ λ³΄μμ νμ κ°μ νμ
μ ν° λμμ΄ λ©λλ€.
μ¬μ¬μ©μ±: κ° μ»΄ν¬λνΈλ₯Ό κ°λ³μ μΈ λͺ¨λλ‘ λΆλ¦¬ν¨μΌλ‘μ¨ μ¬μ¬μ©μ±μ΄ λμμ§λλ€.
λ€λ₯Έ λΆλΆμμ κ°μ μ»΄ν¬λνΈλ₯Ό μ¬μ¬μ©νκΈ° μ¬μμ§λλ€.
μ μ§ λ³΄μ: λ¬Έμ κ° λ°μνμ λ ν΄λΉ μ»΄ν¬λνΈλ§μ μμ νλ©΄ λ©λλ€.
μ 체 μμ€ν
μ 건λλ¦¬μ§ μκ³ λ νΉμ λΆλΆλ§μ μ§μ€μ μΌλ‘ κ΄λ¦¬ν μ μμ΅λλ€.
λ³λ ¬ μμ
: μ¬λ¬ κ°λ°μκ° λμμ λ€λ₯Έ μ»΄ν¬λνΈμμ μμ
ν μ μμ΄ νλ‘μ νΈμ λ³λ ¬ κ°λ°μ΄ μ©μ΄ν©λλ€.
ν¨μ¨μ μΈ λ²μ κ΄λ¦¬: κ° νμΌμ΄ λ
립μ μΌλ‘ λ²μ κ΄λ¦¬λ μ μμ΄, λ³κ²½ μ¬ν μΆμ μ΄ μ¬μμ§λλ€.
ν
μ€νΈ μ©μ΄μ±: κ° μ»΄ν¬λνΈλ₯Ό λ
립μ μΌλ‘ ν
μ€νΈν μ μμ΄,
μ λ ν
μ€νΈμ κ°μ ν
μ€νΈ λ°©λ²λ‘ μ μ μ©νκΈ°μ μ ν©ν©λλ€.
μ΄μ κ°μ νμΌ κ΅¬μ‘°λ ν° κ·λͺ¨μ νλ‘μ νΈλ μ¬λ¬ μ¬λμ΄ νμ
ν λ νΉν μ μ©ν©λλ€.
κ·Έλ¬λ, λ무 λ§μ λΆλ¦¬λ νλ‘μ νΈμ 볡μ‘μ±μ μ¦κ°μν¬ μ μμΌλ,
μ μ ν μμ€μμ μΈλΆνλ₯Ό κ²°μ νλ κ²μ΄ μ€μν©λλ€.
globals.css
.hero-map {
@apply absolute right-0 top-0 h-screen w-screen bg-pattern-2 bg-cover bg-center md:-right-28 xl:-top-60;
}
Hero.tsx
import React from "react";
const Hero = () => {
return(
<section className="max-container padding-container flex
flex-col gap-20 py-10 pb-32 md:gap-28 xl:flex-row border-2
border-red-500">
<div className="hero-map" />
</section>
)
}
export default Hero
<div className="relative z-20 flex flex-1 flex-col xl:w-1/2">
<Image
src="camp.svg"
alt="camp"
width={50}
height={50}
className="absolute left-[-5px] top-[-30px] w-10 lg:w-[50px]"
/>
Tailwind CSS ꡬ문μ λ§μΆ°μ μ΄λ―Έμ§ μμΉλ° ν¬κΈ°μ‘°μ μ ν΄μ€λλ€
<div className="relative z-20 flex flex-1 xl:w-1/2">:
relative: μ΄ divλ ν¬μ§μ
λ 컨ν
μ€νΈλ₯Ό μλμ μΌλ‘ μ€μ ν©λλ€.
μ΄λ λ΄λΆμ μ λ μμΉκ° μ§μ λ μμ(absolute)κ° divλ₯Ό κΈ°μ€μΌλ‘ μμΉνλλ‘ ν©λλ€.
z-20: z-indexκ° 20μΌλ‘ μ€μ λμ΄ λ€λ₯Έ μμλ€κ³Όμ μμ μμλ₯Ό κ²°μ ν©λλ€.
flex: μ΄ divλ νλ μ€ μ»¨ν
μ΄λλ‘ λμν©λλ€.
flex-1: νλ μ€ μμ΄ν
μΌλ‘μ μ¬μ© κ°λ₯ν 곡κ°μ λͺ¨λ μ°¨μ§νλλ‘ ν©λλ€.
xl:w-1/2: νλ©΄μ λλΉκ° xl λΈλ μ΄ν¬ν¬μΈνΈμ λλ¬νλ©΄, μ΄ divμ λλΉλ λΆλͺ¨ 컨ν
μ΄λμ 50%κ° λ©λλ€.
<Image> μ»΄ν¬λνΈ:
src="camp.svg": μ΄λ―Έμ§μ μμ€ νμΌλ‘ camp.svgλ₯Ό μ¬μ©ν©λλ€.
alt="camp": μ΄λ―Έμ§μ λν λ체 ν
μ€νΈλ‘ 'camp'λ₯Ό μ 곡ν©λλ€.
μ΄λ―Έμ§κ° λ‘λλμ§ μμ λλ μ€ν¬λ¦° 리λκ° μ¬μ©λ λ νμλ©λλ€.
width={50}: μ΄λ―Έμ§μ λλΉλ₯Ό 50ν½μ
λ‘ μ€μ ν©λλ€.
height={50}: μ΄λ―Έμ§μ λμ΄λ₯Ό 50ν½μ
λ‘ μ€μ ν©λλ€.
className="absolute left-[-5px] top-[-30px] w-10 lg:w-[50px]":
absolute: μ΄λ―Έμ§κ° μ λ μμΉλ₯Ό κ°μ§κ³ λΆλͺ¨μ relative ν¬μ§μ
λ 컨ν
μ€νΈμ λ°λΌ μμΉνκ² λ©λλ€.
left-[-5px]: μ΄λ―Έμ§μ μΌμͺ½ λ§μ§μ -5ν½μ
λ‘ μ€μ νμ¬ μ€λ₯Έμͺ½μΌλ‘ 5ν½μ
μ΄λν©λλ€.
top-[-30px]: μ΄λ―Έμ§μ μλ¨ λ§μ§μ -30ν½μ
λ‘ μ€μ νμ¬ μλλ‘ 30ν½μ
μ΄λν©λλ€.
w-10: μ΄λ―Έμ§μ λλΉλ₯Ό Tailwind CSSμ w-10 ν΄λμ€μ ν΄λΉνλ ν¬κΈ°λ‘ μ€μ ν©λλ€.
lg:w-[50px]: νλ©΄μ λλΉκ° lg λΈλ μ΄ν¬ν¬μΈνΈμ λλ¬νλ©΄ μ΄λ―Έμ§μ λλΉλ₯Ό 50ν½μ
λ‘ μ€μ ν©λλ€.
CSSμμ top μμ±μ ?
μμμ μλ¨ κ°μ₯μ리μ μμΉλ₯Ό κ²°μ ν©λλ€. μ΄ μμ±μ μ£Όλ‘ position μμ±μ΄
absolute, relative, fixed, λλ stickyλ‘ μ€μ λ μμμ μ¬μ©λ©λλ€.
topμ κ°μ΄ μμμΌ κ²½μ° μμλ κΈ°μ€μ μΌλ‘λΆν° μλλ‘ μ΄λνκ³ ,
μμμΌ κ²½μ° κΈ°μ€μ μΌλ‘λΆν° μλ‘ μ΄λν©λλ€.
μλ₯Ό λ€μ΄, top: 10px;μ μμλ₯Ό κΈ°μ€μ μΌλ‘λΆν°
μλλ‘ 10ν½μ μ΄λμν€λ λ°λ©΄, top: -10px;μ μμλ₯Ό κΈ°μ€μ μΌλ‘λΆν° μλ‘ 10ν½μ μ΄λμν΅λλ€.
λ°λΌμ, μμμμ μ¬μ©λ top-[-30px]λ μμλ₯Ό μλ¨ κΈ°μ€μ μΌλ‘λΆν° 30ν½μ
μλ‘ μ΄λμν€λ κ²μ μλ―Έν©λλ€.
topμ΄ μ΄λ»κ² μλνλμ§λ position μμ±μ λ°λΌ λ¬λΌμ§λλ€:
absolute: μμλ κ°μ₯ κ°κΉμ΄ μμΉ μ§μ λ μ‘°μ μμμ λν΄ μλμ μΌλ‘ λ°°μΉλ©λλ€.
relative: μμλ μκΈ° μμ μ μ μ μμΉλ₯Ό κΈ°μ€μΌλ‘ λ°°μΉλ©λλ€.
fixed: μμλ λ·°ν¬νΈλ₯Ό κΈ°μ€μΌλ‘ λ°°μΉλ©λλ€.
sticky: μμλ μ¬μ©μμ μ€ν¬λ‘€ μμΉμ λ°λΌ relativeμ fixed λ°°μΉ μ¬μ΄λ₯Ό μ νν©λλ€.
κ³ μ°¨ ν¨μ(Higher-Order Function)
체μ΄λ(Chaining)μ νμ©
κ³ μ°¨ ν¨μ(Higher-Order Function): κ³ μ°¨ ν¨μλ λ€λ₯Έ ν¨μλ₯Ό 맀κ°λ³μλ‘ λ°κ±°λ ν¨μλ₯Ό κ²°κ³Όλ‘ λ°ννλ ν¨μμ λλ€. JavaScriptμ .map(), .filter(), .reduce() κ°μ λ°°μ΄ λ©μλλ€μ κ³ μ°¨ ν¨μμ μμ λλ€.
μ΄λ€μ κ° λ°°μ΄ μμμ λν΄ μ½λ°± ν¨μλ₯Ό μ€ννκ³ , κ·Έ κ²°κ³Όλ₯Ό λ°νν©λλ€.
체μ΄λ(Chaining): λ©μλ 체μ΄λμ μ¬λ¬ λ©μλλ₯Ό μ°κ²°νμ¬ νΈμΆνλ νλ‘κ·Έλλ° ν¨ν΄μ
λλ€.
μ΄ ν¨ν΄μ μ¬μ©νλ©΄ μ½λλ₯Ό κ°κ²°νκ³ μ½κΈ° μ½κ² λ§λ€ μ μμ΅λλ€.
μλ₯Ό λ€μ΄, Array(5).fill(1).map(...)μμ fill λ©μλμ map λ©μλκ° μ°μμ μΌλ‘ νΈμΆλλ κ²μ΄ 체μ΄λμ μ’μ μμ λλ€.
<div className="my-11 flex flex-wrap gap-5">
<div className="flex items-center gap-2">
{Array(5).fill(1).map((_, index) => (
<Image
src="/star.svg"
key={index}
alt="star"
width={24}
height={24}
/>
))}
</div>
</div>
μΈλΆ div μμ
className="my-11 flex flex-wrap gap-5": μ΄ ν΄λμ€λ μ¬λ¬ CSS μ€νμΌμ μ μ©ν©λλ€.
my-11: μμ μλμ λ§μ§(margin)μ μ μ©ν©λλ€.
Tailwind CSSμμ 11μ μΌλ°μ μΌλ‘ νΉμ ν¬κΈ°μ 곡κ°μ μλ―Έν©λλ€.
flex: Flexbox λ μ΄μμμ νμ±νν©λλ€.
flex-wrap: Flexbox μμ΄ν
μ΄ μ»¨ν
μ΄λμ λλΉλ₯Ό μ΄κ³Όν κ²½μ° μ€λ°κΏμ νμ©ν©λλ€.
gap-5: Flexbox μμ΄ν
μ¬μ΄μ κ°κ²©μ μ€μ ν©λλ€.
λ΄λΆ div μμ
className="flex items-center gap-2": μ΄ ν΄λμ€ μμ μ¬λ¬ μ€νμΌμ μ μ©ν©λλ€.
flex: Flexbox λ μ΄μμμ νμ±νν©λλ€.
items-center: Flexbox μμ΄ν
λ€μ μΈλ‘ λ°©ν₯ μ€μμ μ λ ¬ν©λλ€.
gap-2: μμ΄ν
μ¬μ΄μ κ°κ²©μ μ€μ ν©λλ€.
Image μ»΄ν¬λνΈ
Array(5).fill(1).map((_, index) => (...): 5κ°μ μμλ₯Ό κ°μ§ λ°°μ΄μ μμ±νκ³ ,
κ° μμμ λν΄ Image μ»΄ν¬λνΈλ₯Ό λ λλ§ν©λλ€.
fill(1)μ λ°°μ΄μ 1λ‘ μ±μ°μ§λ§, μ¬κΈ°μλ μ€μ κ°λ³΄λ€λ λ°λ³΅ νμκ° μ€μν©λλ€.
src="/star.svg": μ΄λ―Έμ§ μμ€λ‘ "/star.svg"λ₯Ό μ¬μ©ν©λλ€.
key={index}: Reactμμ λ°°μ΄μ λ λλ§ν λ κ° μμμ κ³ μ ν ν€λ₯Ό μ 곡ν©λλ€.
alt="star": μ΄λ―Έμ§μ λν λ체 ν
μ€νΈλ‘ "star"λ₯Ό μ¬μ©ν©λλ€.
width={24}μ height={24}: μ΄λ―Έμ§μ λλΉμ λμ΄λ₯Ό κ°κ° 24ν½μ
λ‘ μ€μ ν©λλ€.
μ΄ μ½λλ μ£Όλ‘ λ³ λͺ¨μμ μ΄λ―Έμ§ 5κ°λ₯Ό κ°λ‘λ‘ μ λ ¬νμ¬ νμνλ λ° μ¬μ©λ©λλ€. μλ₯Ό λ€μ΄, λ³μ μμ€ν
μ΄λ νκ° μμ€ν
μ UI μμλ‘ μ¬μ©λ μ μμ΅λλ€.
'ν€(key)'λ Reactκ° DOM λ³κ²½ μ¬νμ ν¨μ¨μ μΌλ‘ κ°μ§νκ³ μ²λ¦¬νλ λ° λμμ΄ λ©λλ€.
ν€μ μ€μμ±:
μ±λ₯ μ΅μ ν: ν€λ Reactμκ² κ° μμκ° μ΄λ»κ² λ³ν, μΆκ°, μ κ±°λμλμ§λ₯Ό μλ €μ€λλ€. μ΄λ₯Ό ν΅ν΄ Reactλ νμν λΆλΆλ§μ μ¬λ λλ§νμ¬ μ±λ₯μ μ΅μ νν©λλ€.
μ¬μ¬μ©κ³Ό μ¬μ λ ¬: λ°°μ΄μ΄λ 리μ€νΈμμ μμμ μμκ° λ³κ²½λκ±°λ, μμκ° μΆκ°/μμ λ λ,
ν€λ₯Ό ν΅ν΄ Reactλ μ΄λ€ μμκ° λ³κ²½λμλμ§ νμ
νμ¬ DOMμ ν¨μ¨μ μΌλ‘ μ
λ°μ΄νΈν©λλ€.
ν€ ν λΉ λ°©λ²:
μ£Όλ‘ λ°μ΄ν°μ κ³ μ ν κ°μ ν€λ‘ μ¬μ©ν©λλ€.
μλ₯Ό λ€μ΄, λ°μ΄ν°λ² μ΄μ€μμ κ°μ Έμ¨ μμ΄ν μ ID, νΉμ 리μ€νΈ μμ΄ν μ΄ κ°λ
κ³ μ ν μμ±κ° λ±μ ν€λ‘ μ¬μ©ν μ μμ΅λλ€.
λ°°μ΄μ μΈλ±μ€λ₯Ό ν€λ‘ μ¬μ©ν μλ μμ§λ§,
리μ€νΈ μμ΄ν μ μμκ° μμ£Ό λ°λκ±°λ μμ΄ν μ΄ μΆκ°/μμ λλ κ²½μ°
λ¬Έμ κ° λ°μν μ μμΌλ―λ‘ κΆμ₯λμ§ μμ΅λλ€.
import { PEOPLE_URL } from "@/constants";
import Image from "next/image";
interface CampProps {
backgroundImage: string;
title: string;
subtitle: string;
peopleJoined: string;
}
//λ©μΈ μ΄λ―Έμ§ νμ΄ν
const CampSite = ({ backgroundImage, title, subtitle, peopleJoined }:
CampProps) => {
return(
<div className={`h-full w-full min-w-[1100px] ${backgroundImage}
bg-cover bg-no-repeat lg:rounded-r-5xl 2xl:rounded-5xl`}>
<div className="flex h-full flex-col items-start justify-between
p-6 lg:px-20 ig:py-10">
<div className="flexCenter gap-4">
<div className="rounded-full bg-green-50 p-4">
<Image
src="folded-map.svg"
alt="map"
width={28}
height={28}
/>
</div>
<div className="flex flex-col gap-1">
<h4 className="bold-18 text-white">{title}</h4>
<p className="regular-14 text-white">{subtitle}</p>
</div>
</div>
/νλ¨ μλΈνμ΄ν
<div className="flexCenter gap-6">
<span className="flex -space-x-4 overflow-hidden">
{PEOPLE_URL.map((url) => (
<Image
className="inline-block h-10 w-10 rounded-full"
src={url}
key={url}
alt="person"
width={52}
height={52}
/>
))}
</span>
<p className="bold-16 md:20 text-white">{peopleJoined}</p>
</div>
</div>
</div>
)
}
ν΄λμ€λͺ
μ¬μ©: Tailwind CSS ν΄λμ€κ° μ¬μ©λμμΌλ©°
ν¬κΈ°μ λ°λΌ λ€λ₯Έ μ€νμΌμ μ μ©νλ λ°μν λμμΈμ΄ μ μ©λμ΄ μμ΅λλ€.
2xl:max-containerμ κ°μ ν΄λμ€λͺ
μ Tailwind CSSμμ μ¬μ©λλ λͺ
λͺ
κ·μΉμ λ°λ₯΄κ³ μμ΅λλ€.
λ°μν λμμΈ: lg:μ xl: μ λμ¬λ₯Ό μ¬μ©νμ¬ λ€μν νλ©΄ ν¬κΈ°μ λμνλ μ€νμΌμ μ μ©νμ΅λλ€.
μ΄λ λ ν° νλ©΄μμ μΉμ
μ λ§μ§, λμ΄ λ° ν¨λ©μ μ‘°μ ν©λλ€.
μ»΄ν¬λνΈ κ΅¬μ±: CampSite μ»΄ν¬λνΈλ μ¬λ¬ κ°μ μμ±μ μ λ¬ λ°μμ μ¬μ©νκ³ μμΌλ©°,
μ¬μ¬μ©μ±μ μν΄ λΆλ¦¬λμ΄ μμ΅λλ€.
κ°λ
μ±: JSXλ κΉλνκ² μ 리λμ΄ μμΌλ©°, ν΄λμ€λͺ
μ΄λ λ€λ₯Έ μμ±λ€μ΄ μ μ λ ¬λμ΄ μμ΄ μ½λμ κ°λ
μ±μ΄ μ’μ΅λλ€.
μ κ·Όμ±: μ΄λ―Έμ§ νκ·Έμ alt μμ±μ΄ ν¬ν¨λμ΄ μμ΄ μ€ν¬λ¦° 리λ μ¬μ©μλ₯Ό μν μ κ·Όμ±μ΄ κ³ λ €λμμ΅λλ€.
μ½λ μ΅μ ν: CampSite μ»΄ν¬λνΈμ λμΌν μμ± κ°μ΄ μ€λ³΅λμ΄ μμ΅λλ€ (titleκ³Ό peopleJoined).
λ§μ½ μ΄ κ°λ€μ΄ λμΌν 컨ν
μ€νΈλ₯Ό κ°μ§λ€λ©΄ μ»΄ν¬λνΈ λ΄μμ κΈ°λ³Έκ°μ μ€μ νλ κ²μ΄ μ’μ μ μμ΅λλ€.
μλ¬ νΈλ€λ§: μ½λμλ μλ¬ νΈλ€λ§ λ‘μ§μ΄ 보μ΄μ§ μμ΅λλ€.
CampSite μ»΄ν¬λνΈκ° μ€ν¨νκ±°λ μμμΉ λͺ»ν μ
λ ₯μ λ°μμ λλ₯Ό λλΉν μ²λ¦¬κ° νμν μ μμ΅λλ€.
μ€ν¬λ‘€λ° μ¨κΈ°κΈ°: hide-scrollbar ν΄λμ€λ μ€νμΌμνΈμ μ μλμ΄ μμ΄μΌ νλ©°,
κΈ°λ³Έ λΈλΌμ°μ μ€ν¬λ‘€λ°λ₯Ό μ¨κΈ°λ μ€νμΌμ ν¬ν¨ν΄μΌ ν©λλ€.
μ΄κ²μ΄ λΉ μ‘λ€λ©΄ μ€ν¬λ‘€λ°κ° μ¬μ ν λ³΄μΌ κ²μ
λλ€.
μΌκ΄μ±: classNameμ κ°μμ ν
μ€νΈ ν¬κΈ°λ₯Ό μ§μ νλ ν΄λμ€μ regular-24, md:regular-32, 2xl:regular-64μ κ°μ μ¬μ©μ μ μ ν΄λμ€κ° 보μ
λλ€. μ΄λ¬ν ν΄λμ€λͺ
μ΄ Tailwind CSSμ κΈ°λ³Έ μ€μ κ³Ό μΌκ΄μ±μ μ μ§νλμ§ νμΈν΄μΌ ν©λλ€.
SEO κ³ λ €μ¬ν: μ΄λ―Έμ§ νκ·Έμ alt μμ±μ΄ μμ΄ μ κ·Όμ±κ³Ό SEOμ λμμ΄ λμ§λ§,
νμ΄μ§μ λ€λ₯Έ λΆλΆμμ SEOλ₯Ό κ³ λ €ν μμ(μ: λ©ν νκ·Έ, ꡬ쑰νλ λ°μ΄ν°)κ° μλμ§ νμΈν΄μΌ ν©λλ€.
μ λ°μ μΌλ‘ μ½λλ μ μμ±λμ΄ μμ§λ§, μ€μ νλ‘λμ
νκ²½μμλ μλ¬ μ²λ¦¬, SEO μ΅μ ν λ° μ¬μ©μ μ μ ν΄λμ€μ μΌκ΄μ± λ±μ μΆκ°λ‘ κ³ λ €ν΄μΌ ν μ μμ΅λλ€.
Guide.tsx
import Image from 'next/image'
import React from 'react'
const Guide = () => {
return (
<section className='flexCenter flex-col'>
<div className='padding-container
max-container w-full pb-24'>
<Image src="/camp.svg" alt="camp" width={50} height={50}/>
<p className='uppercase regular-18 -mt-1 mb-3 text-green-50'>
We are here for you
</p>
<div className='flex flex-wrap justify-between gap-5 lg:gap-10'>
<h2 className='bold-40 lg:bold-64 xl:max-w-[390px]'>Guide You to Easy path</h2>
<p className='regular-16 text-gray-30 xl:max-w-[520px]'>Only with the hilink application you will no longer gat
lost and get lost again, because we already support offline
maps when there is no internet connection in the field.
Invite your friends, relatives and friends to have fun
in the wilderness through the valley and reach the top of the
mountain</p>
</div>
</div>
<div className='flexCenter max-container relative w-full'>
<Image
src='/boat.png'
alt='boat'
width={1440}
height={580}
className='w-fill object-cover object-center 2xl:rounded-5xl'
/>
<div className='absolute flex bg-white py-8 pl-5 pr-7 gap-3
rounded-3xl border shadow-md md:left-[5%] lg:top-20'>
<Image
src='/meter.svg'
alt='meter'
width={16}
height={158}
className='h-full w-auto'/>
<div className='flexBetween flex-col'>
<div className='flex w-full flex-col'>
<div className='flexBetween w-full'>
<p className='regular-16 text-gray-20'>Destination</p>
<p className='bold-16 text-green-50'>48 min</p>
</div>
<p className='bold-20 mt-2'>Aguas Calientex</p>
</div>
<div className='flex w-full flex-col'>
<p className='regular-16 text-gray-20'>Start track</p>
<p className='bold-20 mt-2 whitespace-nowrap'>Wonorejo Pasuruan</p>
</div>
</div>
</div>
</div>
</section>
)
}
export default Guide
ꡬ쑰: μ»΄ν¬λνΈλ λ κ°μ μ£Όμ divλ‘ κ΅¬μ±λμ΄ μμΌλ©°,
λͺ¨λ μ΅λ λλΉλ₯Ό κ°μ§κ³ μ€μμ μμΉνλλ‘ μ€νμΌλ§ λμ΄ μμ΅λλ€.
μ΄λ¬ν ꡬ쑰λ μΉμ
μ λͺ
ννκ² κ΅¬λΆνκ³ κ°λ
μ±μ λμ
λλ€.
μ€νμΌλ§κ³Ό μΌκ΄μ±: ν΄λμ€λͺ
μ μΌκ΄μ μΌλ‘ μ¬μ©λμμΌλ©°, μλ―Έκ° λͺ
νν©λλ€.
flexCenter, padding-container, max-container λ±μ ν΄λμ€λ ν΄λΉ μ€νμΌμνΈμμ
μ¬μ©μ μ μ ν΄λμ€λ‘ μ μλμ΄ μμ΄μΌ ν©λλ€.
λ°μν λμμΈ: lg:μ xl: μ λμ¬λ₯Ό μ¬μ©ν΄ λμνλ λΈλ μ΄ν¬ν¬μΈνΈμ λ°λ₯Έ μ€νμΌμ μ μ©νμ΅λλ€.
μ΄λ₯Ό ν΅ν΄ λ€μν νλ©΄ ν¬κΈ°μ μ μ ν λ μ΄μμμ μ 곡ν©λλ€.
Next.jsμ Image μ»΄ν¬λνΈ: μ΄λ―Έμ§ μ΅μ νλ₯Ό μν΄ next/image μ»΄ν¬λνΈλ₯Ό μ¬μ©νμ΅λλ€. μ΄ μ»΄ν¬λνΈλ μλμΌλ‘ μ΄λ―Έμ§λ₯Ό μ΅μ ννκ³ μ±λ₯μ ν₯μμν΅λλ€.
μ κ·Όμ±: λͺ¨λ μ΄λ―Έμ§μ alt μμ±μ΄ μ 곡λμ΄ μμ΄, μ€ν¬λ¦° 리λ μ¬μ©μμκ² λμμ΄ λ©λλ€. μ΄λ μΉ μ κ·Όμ±μ λμ΄λ μ€μν λΆλΆμ
λλ€.
μλ¬ νΈλ€λ§: μ»΄ν¬λνΈ λ΄μμ μ΄λ―Έμ§ κ²½λ‘κ° μλͺ»λκ±°λ μ΄λ―Έμ§ λ‘λ©μ μ€ν¨νμ κ²½μ°λ₯Ό λλΉν μλ¬ νΈλ€λ§ λ‘μ§μ΄ 보μ΄μ§ μμ΅λλ€.
ν
μ€νΈ μ€λ₯: gat lostλ get lostλ‘ μμ ν΄μΌ ν©λλ€. λν, friendsκ° λ λ² λ°λ³΅λλλ°, μ΄λ μλμ μΈ μ€λ³΅μ΄ μλλΌλ©΄ μμ ν΄μΌ ν©λλ€.
ν΄λμ€λͺ
μ€λ₯: -mt-1μ μμ λ§μ§μ λνλ
λλ€. μ΄ ν΄λμ€κ° μλν λλ‘ μλνλμ§ νμΈν΄μΌ ν©λλ€.
SEO κ³ λ €μ¬ν: μ΄λ―Έμ§μ alt νκ·Έκ° μμ§λ§, νμ΄μ§μ λ€λ₯Έ SEO κ΄λ ¨ μμλ€(μ: λ©ν νκ·Έ)μ΄ κ³ λ €λμλμ§λ λ³Ό μ μμ΅λλ€.
λ μ΄μμ μ΅μ ν: max-containerλ μ¬λ¬ κ³³μμ μ¬μ©λκ³ μλλ°, μ΄λ μΌκ΄λ μ΅λ λλΉλ₯Ό μ€μ νλ μ¬μ©μ μ μ ν΄λμ€λ‘ μΆμ λ©λλ€. μ»΄ν¬λνΈμ ν¬κΈ°λ₯Ό μΌκ΄λκ² μ μ§νλλ° λμμ΄ λ©λλ€.
Features
import { FEATURES } from '@/constants'
import Image from 'next/image'
import React from 'react'
const Features = () => {
return(
<section className='flex-col flexCenter overflow-hidden
bg-feature-bg bg-center bg-no-repeat py-24'>
<div className='max-container padding-container relative w-full flex justify-end'>
<div className='flex flex-1 lg:min-h-[900px]'>
<Image
src='/phone.png'
alt='phone'
width={440}
height={1000}
className='feature-phone'/>
</div>
<div className='z-20 flex w-full flex-col lg:w-[60%]'>
<div className='relative'>
<Image
src='camp.svg'
alt='camp'
width={50}
height={50}
className='absolute left-[-5px] top-[-28px] w-10 lg:w-[50px]'/>
<h2 className='bold-40 lg:bold-64'>Our Features</h2>
</div>
<ul className='mt-10 grid gap-10 md:grid-cols-2 lg:mg-20 ib:gap-20'>
{FEATURES.map((feature) => (
<FeatureItem
key={feature.title}
title={feature.title}
icon={feature.icon}
description={feature.description}/>
))}</ul>
</div>
</div>
</section>
)
}
type FeatureItem = {
title: string;
icon: string;
description: string;
}
const FeatureItem = ({ title, icon, description } :
FeatureItem ) =>{
return (
<li className='flex w-full flex-1 flex-col items-start'>
<div className='rounded-full p-4 ig:p-7 bg-green-50'>
<Image src={icon} alt='map' width={28} height={28}/>
</div>
<h2 className='bold-20 lg:bold-32 mt-5 capitalize'>
{title}
</h2>
<p className='regular-16 mt-5 bg-white/80 text-gray-30 ig:mt-[30px] ig:bg-none'>
{description}
</p>
</li>
)
}
export default Features
μ½λ ꡬ쑰:
import λ¬Έμ μ¬μ©νμ¬ νμν λͺ¨λκ³Ό μ»΄ν¬λνΈλ₯Ό λΆλ¬μ€κ³ μμ΅λλ€.
μλ₯Ό λ€μ΄, Image μ»΄ν¬λνΈλ Next.jsμ μ΄λ―Έμ§ μ΅μ ν κΈ°λ₯μ μ¬μ©νκΈ° μν΄ λΆλ¬μ΅λλ€.
Features μ»΄ν¬λνΈλ νλ©΄μ νΉμ§λ€μ 보μ¬μ£Όλ μ£Όμ λΆλΆμ
λλ€.
FeatureItem νμ
μ κ° νΉμ§ νλͺ©μ μμ±μ μ μν©λλ€ (title, icon, description).
FeatureItem μ»΄ν¬λνΈλ κ°λ³ νΉμ§ νλͺ©μ λ λλ§νλ λ° μ¬μ©λ©λλ€.
μ€νμΌλ§:
Tailwind CSSλ₯Ό μ¬μ©νμ¬ μ€νμΌμ μ μ© , μλ₯Ό λ€μ΄, flex, w-full, bg-green-50 λ±μ ν΄λμ€λͺ
μ μ¬μ©νμ΅λλ€.
λ°μν λμμΈμ μν΄ λ€μν ν¬κΈ°μ νλ©΄μ λμνλ ν΄λμ€(lg, md, ig)λ₯Ό μ¬μ©νμ΅λλ€.
κΈ°λ₯:
FEATURES λ°°μ΄μμ κ° νΉμ§ νλͺ©μ λ°μ΄ν°λ₯Ό κ°μ Έμ FeatureItem μ»΄ν¬λνΈμ μ λ¬ν©λλ€.
κ° FeatureItemμ μ λͺ©, μμ΄μ½, μ€λͺ
μ ν¬ν¨ν©λλ€.
liνκ·Έμ μ€μμ±
<li> νκ·Έλ HTMLμμ "list item"μ μ½μλ‘, λͺ©λ‘ νλͺ©μ λνλ΄λ λ° μ¬μ©λ©λλ€. μ΄ νκ·Έλ μ£Όλ‘ <ul> (unordered list, μ¦ μμ μλ λͺ©λ‘) λλ <ol> (ordered list, μμ μλ λͺ©λ‘) νκ·Έ λ΄λΆμ μμΉνμ¬ λͺ©λ‘μ κ° νλͺ©μ ꡬλΆν©λλ€.
FeatureItem μ»΄ν¬λνΈμμ <li> νκ·Έλ₯Ό μ¬μ©νλ μ΄μ λ λ€μκ³Ό κ°μ΅λλ€:
μλ―Έλ‘ μ HTML ꡬ쑰: <li> νκ·Έλ μΉ λ¬Έμ λ΄μμ λͺ©λ‘ νλͺ©μ μλ―Έλ‘ μ μΌλ‘ λνλ΄λ λ° μ ν©ν©λλ€.
μ΄λ μΉ νμ΄μ§μ ꡬ쑰λ₯Ό λ λͺ
ννκ² λ§λ€κ³ , κ²μ μμ§ μ΅μ ν(SEO)μ μ κ·Όμ±μ κΈμ μ μΈ μν₯μ λ―ΈμΉ©λλ€.
μ€νμΌλ§κ³Ό μ‘°μ§: λͺ©λ‘ νλͺ©μ <li> νκ·Έλ‘ κ΅¬λΆν¨μΌλ‘μ¨, κ° νλͺ©μ λν μ€νμΌλ§κ³Ό κ΄λ¦¬κ° μ©μ΄ν΄μ§λλ€.
CSSλ₯Ό μ¬μ©νμ¬ κ° λͺ©λ‘ νλͺ©μ λ μ΄μμ, μ¬λ°±, λ°°κ²½ λ±μ ν΅μ ν μ μμ΅λλ€.
μ κ·Όμ±: μ€ν¬λ¦° 리λμ κ°μ 보쑰 κΈ°μ μ <li> νκ·Έλ₯Ό μ¬μ©νμ¬ λͺ©λ‘ νλͺ©μ μ¬μ©μμκ² λͺ
ννκ² μ λ¬ν©λλ€.
μ΄λ μκ° μ₯μ κ° μλ μ¬μ©μλ€μ΄ μΉ μ½ν
μΈ λ₯Ό λ μ½κ² νμνκ³ μ΄ν΄ν μ μκ² ν©λλ€.
μ½λμ λ§₯λ½μμ, FeatureItem μ»΄ν¬λνΈλ μλ§λ <ul> λλ <ol> νκ·Έ λ΄λΆμμ μ¬λ¬ λ² νΈμΆλμ΄ κ°κ°μ νΉμ§λ€μ λͺ©λ‘ ννλ‘ νμνκ³ μ νλ λͺ©μ μΌλ‘ μ¬μ©λλ κ²μΌλ‘ 보μ
λλ€.
λν μ½λμ μμμμλ€μ΄ λ§μμ§μλ‘ flexνκ·Έλ₯Ό μ κ²½μ¨μ μ¬μ©μ ν΄μ€μΌμ§λ§ μνλ μμΉμ
μμꡬνμ΄ κ°λ₯ν©λλ€
alt μμ±μ μ΄λ¦μ μ§μ νλ μ£Όμ μ΄μ λ?
μ κ·Όμ±(Accessibility): alt μμ±μ μκ° μ₯μ κ° μλ μ¬μ©μλ€μ΄ μ€ν¬λ¦° 리λλ₯Ό ν΅ν΄ μ΄λ―Έμ§μ λ΄μ©μ μ΄ν΄ν μ μλλ‘ λμ΅λλ€. μ€ν¬λ¦° 리λλ alt μμ±μ ν
μ€νΈλ₯Ό μ½μ΄ μ¬μ©μμκ² μ΄λ―Έμ§μ λ΄μ©μ μ λ¬ν©λλ€.
μ΄λ―Έμ§ λ‘λ© μ€ν¨ μ λ체 ν
μ€νΈ: λ§μ½ μ΄λ―Έμ§κ° λ‘λ©λμ§ μμ κ²½μ°, alt μμ±μ μ§μ λ ν
μ€νΈκ° μ΄λ―Έμ§ λμ νμλ©λλ€. μ΄λ μ¬μ©μμκ² μ΄λ―Έμ§μ λ΄μ©μ μ λ¬νλ λ° λμμ΄ λ©λλ€.
κ²μ μμ§ μ΅μ ν(SEO): κ²μ μμ§μ μ΄λ―Έμ§μ alt ν
μ€νΈλ₯Ό λΆμνμ¬ μ΄λ―Έμ§μ λ΄μ©κ³Ό κ΄λ ¨μ±μ μ΄ν΄ν©λλ€. μ΄λ μ΄λ―Έμ§κ° κ²μ κ²°κ³Όμ μ μ νκ² λνλλλ‘ νλ λ° μ€μν μν μ ν©λλ€.
Footer
import { FOOTER_CONTACT_INFO, FOOTER_LINKS, SOCIALS } from '@/constants'
import Image from 'next/image'
import Link from 'next/link'
import React from 'react'
const Footer = () => {
return(
<footer className='flexCenter mb-24'>
<div className='padding-container max-container flex w-full
flex-col gap-14'>
<div className='flex flex-col items-start justify-center
gap-[10%] md:flex-row'>
<Link href='/' className='mb-10'>
<Image src='hilink-logo.svg' alt='logo' width={74} height={29} />
</Link>
<div className='flex flex-wrap gap-10 sm:justify-between md:flex-1'>
{FOOTER_LINKS.map((columns) => (
<FooterColumn title={columns.title}>
<ul className='regular-14 flex flex-col gap-4 text-gray-30'>
{columns.links.map((link) => (
<Link href='/' key={link}>
{link}
</Link>
))}
</ul>
</FooterColumn>
))}
<div className='flex flex-col gap-5'>
<FooterColumn title={FOOTER_CONTACT_INFO.title}>
<ul className='regular-14 flex gap-4 text-gray-30'>
{FOOTER_CONTACT_INFO.links.map((link) => (
<Link
href='/'
key={link.label}
className='flex gap-4 md:flex-col lg:flex-row'
>
<p className='Whitespace-nowrap'>
{link.label}:
</p>
<p className='medium-14 whitespace-nowrap text-blue-70'>
{link.value}
</p>
</Link>
))}
</ul>
</FooterColumn>
</div>
<div className='flex flex-col gap-5'>
<FooterColumn title={SOCIALS.title}>
<ul className='regular-14 flex gap-4 text-gray-30'>
{SOCIALS.links.map((link) => (
<Link href='/' key={link}>
<Image src={link} alt='logo' width={24}
height={24} />
</Link>
))}
</ul>
</FooterColumn>
</div>
</div>
</div>
</div>
</footer>
)
}
type FooterColumnProps = {
title: string;
children: React.ReactNode;
}
const FooterColumn = ({ title, children} : FooterColumnProps ) =>{
return(
<div className='flex flex-col gap-5'>
<h4 className='bold-18 whitespace-nowrap'>{title}</h4>
{children}
</div>
)
}
export default Footer
// NAVIGATION
export const NAV_LINKS = [
{ href: '/', key: 'home', label: 'Home' },
{ href: '/', key: 'how_hilink_work', label: 'How Hilink Work?' },
{ href: '/', key: 'services', label: 'Services' },
{ href: '/', key: 'pricing ', label: 'Pricing ' },
{ href: '/', key: 'contact_us', label: 'Contact Us' },
];
// CAMP SECTION
export const PEOPLE_URL = [
'/person-1.png',
'/person-2.png',
'/person-3.png',
'/person-4.png',
];
// FEATURES SECTION
export const FEATURES = [
{
title: 'Real maps can be offline',
icon: '/map.svg',
variant: 'green',
description:
'We provide a solution for you to be able to use our application when climbing, yes offline maps you can use at any time there is no signal at the location',
},
{
title: 'Set an adventure schedule',
icon: '/calendar.svg',
variant: 'green',
description:
"Schedule an adventure with friends. On holidays, there are many interesting offers from Hilink. That way, there's no more discussion",
},
{
title: 'Technology using augment reality',
icon: '/tech.svg',
variant: 'green',
description:
'Technology uses augmented reality as a guide to your hiking trail in the forest to the top of the mountain. Already supported by the latest technology without an internet connection',
},
{
title: 'Many new locations every month',
icon: '/location.svg',
variant: 'orange',
description:
'Lots of new locations every month, because we have a worldwide community of climbers who share their best experiences with climbing',
},
];
// FOOTER SECTION
export const FOOTER_LINKS = [
{
title: 'Learn More',
links: [
'About Hilink',
'Press Releases',
'Environment',
'Jobs',
'Privacy Policy',
'Contact Us',
],
},
{
title: 'Our Community',
links: ['Climbing xixixi', 'Hiking hilink', 'Hilink kinthill'],
},
];
export const FOOTER_CONTACT_INFO = {
title: 'Contact Us',
links: [
{ label: 'Admin Officer', value: '123-456-7890' },
{ label: 'Email Officer', value: 'hilink@akinthil.com' },
],
};
export const SOCIALS = {
title: 'Social',
links: [
'/facebook.svg',
'/instagram.svg',
'/twitter.svg',
'/youtube.svg',
'/wordpress.svg',
],
};
Footer μ»΄ν¬λνΈ:
Footer ν¨μν μ»΄ν¬λνΈλ μΉμ¬μ΄νΈμ νλ¨ λΆλΆμ ꡬμ±ν©λλ€.
Tailwind CSSλ₯Ό νμ©νμ¬ μ€νμΌλ§μ΄ μ μ©λμ΄ μμΌλ©°, flexCenter, mb-24, flex, gap-14 λ±μ ν΄λμ€λ₯Ό μ¬μ©ν©λλ€.
μμ λ°μ΄ν° μ¬μ©:
@/constantsμμ FOOTER_LINKS, FOOTER_CONTACT_INFO, SOCIALS λ±μ λ°μ΄ν°λ₯Ό λΆλ¬μμ μ¬μ©ν©λλ€.
μ΄λ¬ν λ°©μμ μ½λμ κ΄λ¦¬λ₯Ό μ©μ΄νκ² νλ©° μ¬μ¬μ©μ±μ λμ¬μ€λλ€.
FooterColumn μ»΄ν¬λνΈ:
FooterColumn μ»΄ν¬λνΈλ κ° νΈν° μ΄(μΉΌλΌ)μ ꡬνν©λλ€. titleκ³Ό children propsλ₯Ό λ°μ μ¬μ©ν©λλ€.
μ΄ μ»΄ν¬λνΈλ νΈν°μ κ° μΉμ
μ λͺ©κ³Ό λ΄μ©(μμ μμ)μ νμν©λλ€.
Linkμ Image μ»΄ν¬λνΈ:
Next.jsμ Linkμ Image μ»΄ν¬λνΈλ₯Ό μ¬μ©νμ¬ μ΄λ―Έμ§μ λ§ν¬λ₯Ό μ΅μ νλ λ°©μμΌλ‘ νμν©λλ€.
맡νλ λ°μ΄ν° λ λλ§:
FOOTER_LINKS λ°°μ΄μ κ° νλͺ©μ FooterColumn μ»΄ν¬λνΈλ₯Ό μ¬μ©νμ¬ λ λλ§ν©λλ€.
FOOTER_CONTACT_INFOμ SOCIALSμ λ°μ΄ν°λ μ μ¬ν λ°©μμΌλ‘ μ²λ¦¬ν©λλ€.
λ°μν λμμΈ:
md:flex-rowμ κ°μ Tailwind CSS ν΄λμ€λ₯Ό μ¬μ©νμ¬ μ€λ¨μ (breakpoint)μ λ°λΌ λ μ΄μμμ μ‘°μ ν©λλ€.
μ΄ μ½λλ λͺ¨λνλ μ κ·Ό λ°©μμ ν΅ν΄ μ¬μ¬μ© κ°λ₯ν μ»΄ν¬λνΈλ₯Ό μμ±νκ³ , μΈλΆ λ°μ΄ν° νμΌμ ν΅ν΄ μ½ν μΈ λ₯Ό κ΄λ¦¬νλ ν¨κ³Όμ μΈ λ°©λ²μ 보μ¬μ€λλ€. μ΄λ μ μ§λ³΄μκ° μ©μ΄νλ©°, μ½λμ κ°λ μ±μ ν₯μμν€λ λ°©λ²μ λλ€.
<div className='border bg-gray-20' />
<p className='regular-14 w-full text-center text-gray-30'>
2023 Hilink | All rights reserved
</p>
</div>
μ΅νλ¨ μ€λͺ λκΉμ§ μ μν μ΄νμ κΈ°μ΄ μΊ νμΉμ¬μ΄νΈ ꡬνμ λ§μΉκ² μ΅λλ€ :)!