μΊ ν•‘μ‚¬μ΄νŠΈ 정적웹 개발둜 Node.js μž…λ¬Έλ° uiux λΆ„μ„ν•΄λ³΄κΈ°πŸ˜‰

2023. 12. 7. 14:55ㆍDevelopmentπŸ‘©πŸ»‍🦳/Static Web

https://github.com/mijoungkim/Travel-Website-React.js-Next.js-13-Tailwind-CSS

 

GitHub - mijoungkim/Travel-Website-React.js-Next.js-13-Tailwind-CSS

Contribute to mijoungkim/Travel-Website-React.js-Next.js-13-Tailwind-CSS development by creating an account on GitHub.

github.com

μš°μ„  터미널에 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>

μ΅œν•˜λ‹¨ μ„€λͺ…λž€κΉŒμ§€ μ œμž‘ν•œ 이후에 기초 μΊ ν•‘μ›Ήμ‚¬μ΄νŠΈ κ΅¬ν˜„μ€ λ§ˆμΉ˜κ² μŠ΅λ‹ˆλ‹€ :)!

μ›Ήμ‚¬μ΄μ¦ˆμ˜ κ΅¬ν˜„ν™”λ©΄μž…λ‹ˆλ‹€

 

μ•±μ‚¬μ΄μ¦ˆμ˜ κ΅¬ν˜„ν™”λ©΄μž…λ‹ˆλ‹€