Nuxt의 탄생 배경 및 필요성
Nuxt 3의 주요 특징
Nuxt의 목표
Node.js
패키지 관리자
Nuxt 3란 무엇인가?
Nuxt의 역할과 특징
초기 웹
웹의 성장과 변화
Vue.js의 등장
미래를 향한 진화
웹 개발은 단순한 정보 제공에서 사용자 경험 창출로 변화해왔으며, Vue.js는 이러한 변화에 적응하고 개발자들의 복잡성을 해결해주는 중요한 도구로 자리 잡았습니다. 하지만, 발전의 여지는 항상 존재하며, Nuxt와 같은 도구가 이를 이어받아 더 나은 미래를 열어가고 있습니다.
Nuxt가 등장하게 된 배경
Nuxt의 탄생
Nuxt가 제공하는 혁신
Nuxt는 Vue.js 개발의 효율성과 생산성을 극대화하기 위해 탄생한 프레임워크입니다. 반복적인 작업을 줄이고, 개발자가 본질적인 코딩에 집중할 수 있도록 돕는 Nuxt는 현대 웹 개발에 필수적인 도구로 자리 잡았습니다.
Nuxt의 필요성
Nuxt의 주요 특징
Nuxt의 효과
Nuxt 3를 이해하는 중요성
Nuxt 3의 아키텍처와 원칙
SSR
)과 클라이언트사이드 렌더링(SPA
) 모두 지원.Nuxt 3의 기반: Vue.js
Nuxt 3가 Vue를 기반으로 선택한 이유
범용 렌더링(Universal Rendering)의 개념
정적 사이트 생성(SSG)의 개념
Nuxt로 구현되는 이점
Nuxt의 범용 렌더링과 정적 사이트 생성 기능은 웹 애플리케이션 개발에서 속도, 보안, 동적 상호작용의 균형을 제공하며, 최적화된 사용자 경험을 구현하는 데 필수적인 도구입니다.
웹 개발에서 성능의 중요성
Nuxt 3의 주요 성능 최적화 기법
Nuxt와 Vite의 역할
Nuxt 3 성능 최적화의 효과
Nuxt 3는 코드 스플리팅, 트리 쉐이킹, 링크 프리페칭과 같은 최신 기술을 활용하여 최상의 성능을 제공하며, Vite와의 통합으로 개발 과정 또한 최적화됩니다. 이로써 사용자와 개발자 모두에게 더 나은 웹 경험을 제공합니다.
설치와 설정의 중요성
Nuxt 3 설치를 위한 사전 준비
Node.js 설치
Nuxt 3 실행을 위해 Node.js는 필수.
버전: 최소 v18.10.0 이상.
버전 확인: 터미널에서 아래 명령어 실행:
node -v
결과로 버전 번호가 표시되면 설치 완료.
버전이 낮거나 설치되지 않았다면 Node.js 공식 사이트에서 최신 버전 다운로드 및 설치.
패키지 관리자 설치
기본적으로 Node.js 설치 시 npm 제공.
추천 패키지 관리자: pnpm (더 빠르고 효율적).
설치 명령어:
npm install -g pnpm
# 프로젝트 생성
# 패키지 관리자로 pnpm 선택
npx nuxi init test
cd <프로젝트이름>
pnpm i
# 로컬 서버 실행
pnpm dev
기본 디렉토리 및 파일 구조
Nuxt 3 프로젝트 구조의 특징
pages
, plugins
, middleware
디렉토리가 추가되어 확장 가능.1. 기본 디렉토리 및 파일
├── README.md: 프로젝트 소개 및 가이드 제공
├── server: 사용자 정의 서버 로직 저장
├── tsconfig.json: TypeScript 설정 파일
├── package.json: 스크립트 및 의존성 관리
│ ├── dev: 개발 환경 실행
│ ├── build: 빌드 실행
│ ├── generate: 정적 페이지 생성
│ ├── preview: 정적 사이트 미리보기
│ └── postinstall: 설치 후 Nuxt 환경 설정
├── .npmrc: npm 설정
├── public: 정적 자산 저장
├── pnpm-lock.yaml: 패키지 설치 일관성 보장
├── nuxt.config.ts: 프로젝트 주요 설정 파일
└── App.vue: 애플리케이션 루트 컴포넌트
2. Nuxt 구조의 확장성
└── pages, plugins, middleware 등 추가 가능
└── 프로젝트 성장과 함께 디렉토리 확장
Nuxt 3의 기본 프로젝트 구조는 명확하고 효율적으로 설계되어 있으며, 프로젝트 확장에 적합한 유연성을 제공합니다. 이를 기반으로 프로젝트를 시작하면 빠르게 애플리케이션 개발에 집중할 수 있습니다.
Nuxt 3의 역할과 핵심 기능
Nuxt 3 환경 설정 및 첫 애플리케이션 제작
Nuxt 3 프로젝트 구조
Nuxt 3 환경 설정에 필요한 사전 조건은 무엇인가?
새로운 Nuxt 3 프로젝트를 초기화하는 명령어는?
명령어:
npx nuxi init test
Nuxt 3에서 서버사이드 렌더링(SSR)의 이점은?
Nuxt 프로젝트에서 nuxt.config.ts 파일의 역할은?
Nuxt 3 애플리케이션의 기본 프로젝트 구조는?
Nuxt 3가 Vue.js를 기반으로 함으로써 얻는 이점은?
Nuxt 3는 SSG를 어떻게 처리하는가?
정적 사이트를 사전 생성하여 빠르고 안전한 콘텐츠 제공.
동적 기능과 정적 성능을 결합.
명령어
pnpm generate
Nuxt 3는 Vue.js 기반의 직관적인 프레임워크로, SSR 및 SSG와 같은 강력한 기능을 제공하며 효율적인 개발 환경을 지원합니다. 위 질문을 통해 핵심 개념을 반복 학습하고 실전 프로젝트에 적용할 수 있습니다.
Nuxt 3와 Tailwind CSS의 결합
포트폴리오 제작 목표
이 장에서 다룰 주요 내용
Tailwind CSS의 개념
Tailwind CSS 사용 이유
# tailwind 설치
pnpm i -D @nuxtjs/tailwindcss
// nuxt.config.ts
// tailwind 설정
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['@nuxtjs/tailwindcss'],
})
// tailwind.config.js 생성
// tailwind.config.js
export default {
// Your Tailwind CSS custom configuration goes here
}
또는 아래와 같이 명령어로 tailwind.config.js 파일 생성 가능.
# tailwind 초기화
pnpm dlx tailwindcss init
// tailwind.config.js
export default {
content: [],
theme: {
extend: {
colors: {
primary: '#42B883',
secondary: '#35495E',
neutral: '#F7F9FA'
},
container: {
center: true,
padding: {
DEFAULT: '1.5rem',
lg: '4rem',
xl: '4rem',
'2xl': '4rem'
},
screens: {
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
'2xl': '1400px'
}
}
}
},
plugins: []
}
더 깊이 파고들어, 내가 선택한 구성의 각 부분이 포트폴리오 전반에 걸쳐 매끄럽고 일관적이며 Vue 테마의 미학을 보장하는 방법을 살펴보겠습니다.
Tailwind CSS 색상 커스터마이징
Tailwind CSS 구성 파일에서 색상 추가 및 수정:
module.exports = {
theme: {
extend: {
colors: {
primary: '#42b883', // Vue.js의 녹색 계열
},
},
},
};
컨테이너 커스터마이징
화면 단위(Screen Breakpoints) 설정
역할:
Bootstrap의 브레이크포인트와 조정:
module.exports = {
theme: {
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
},
},
};
Tailwind css 테스트
<template>
<div class="text-7xl text-primary">Nuxt 3 + Tailwind CSS!</div>
</template>
포트폴리오의 시각적 기초 구축
폰트 커스터마이징
Tailwind CSS를 활용한 폰트 설정
Tailwind CSS의 구성 파일에서 프로젝트에 적합한 폰트 추가:
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'ui-sans-serif', 'system-ui'],
serif: ['Merriweather', 'serif'],
},
},
},
};
예시 폰트:
Google Fonts 통합
index.html 또는 Nuxt 설정 파일에서 Google Fonts 연결:
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Merriweather:wght@400;700&display=swap" rel="stylesheet">
적용 방법
Tailwind 클래스 사용:
<h1 class="font-sans text-4xl">포트폴리오 제목</h1>
<p class="font-serif text-lg">소개 글...</p>
레이아웃 설계
Tailwind의 유틸리티 클래스를 활용한 구조 생성
<template>
<div class="container mx-auto p-6">
<header class="text-center">
<h1 class="text-5xl font-bold">포트폴리오</h1>
<p class="text-lg text-gray-600">나의 작품과 기술을 소개합니다.</p>
</header>
<main class="mt-10">
<section>
<h2 class="text-3xl font-semibold">프로젝트</h2>
<!-- 프로젝트 목록 추가 -->
</section>
</main>
</div>
</template>
웹사이트의 레이아웃과 폰트 커스터마이징은 포트폴리오의 첫인상을 좌우하는 중요한 단계입니다. 다음 단계에서는 레이아웃을 더욱 발전시키고, 포트폴리오 구성 요소를 추가하여 사용자 경험을 완성해 나갑니다.
Lato 폰트를 사용하는 이유
Nuxt 3에 Lato 폰트 추가 방법
옵션 1: nuxt.config.ts에 직접 추가
Google Fonts에서 Lato 폰트 링크를 가져와 Nuxt 구성 파일에 추가.
export default defineNuxtConfig({
modules: ['@nuxtjs/tailwindcss'],
app: {
head: {
link: [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{ rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: '' },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Lato:wght@100;300;400;700;900&display=swap' }
]
}
}
});
코드 설명:
옵션 2: @nuxtjs/google-fonts 모듈 사용
pnpm i -D @nuxtjs/google-fonts
export default defineNuxtConfig({
modules: ['@nuxtjs/tailwindcss', '@nuxtjs/google-fonts'],
googleFonts: {
families: {
Lato: [100, 300, 400, 700, 900]
}
}
});
Tailwind CSS에서 Lato 폰트 적용
tailwind.config.js 수정:
Tailwind CSS에서 Lato 폰트를 기본 sans-serif로 설정:
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Lato', 'sans-serif']
}
}
}
};
효과:
Lato 폰트를 Nuxt 3와 Tailwind CSS에 통합하면 시각적 일관성과 가독성이 향상되며, 포트폴리오의 텍스트 스타일을 전문적으로 구성할 수 있습니다. 이제 기본 레이아웃 설계로 이동하여 포트폴리오의 구조를 완성해 나갑니다.
googleFonts does not exist in type InputConfig<NuxtConfig, ConfigLayerMeta> 이런 에러가 발생할시
rm -rf node_modules 실행 후
pnpm i로 패키지 재설치
<template>
<template>
<div class="container mx-auto bg-neutral-200">
<header class="bg-secondary text-white p-6">
<div class="container mx-auto flex justify-between items-center">
<div class="text-xl font-light uppercase">Nuxt Test</div>
<nav class="hidden md:block">
<ul class="flex gap-x-4">
<li v-for="item in headerLinks" :key="item.name">
<a :href="item.link" class="transition hover:text-primary">
{{ item.name }}
</a>
</li>
</ul>
</nav>
</div>
</header>
</div>
</template>
</template>
<script setup lang="ts">
const headerLinks = [
{ name: 'Nuxt', link: '#nuxt' },
{ name: 'Basic', link: '#basic' },
{ name: 'Advanced', link: '#advanced' }
]
</script>
Nuxt 3의 assets와 public 디렉토리 차이
assets/ 디렉토리:
<template>
<template>
<div class="container mx-auto">
<header class="bg-secondary text-white p-6 bg-neutral-950">
<div class="container mx-auto flex justify-between items-center">
<div class="text-xl font-light uppercase">Nuxt Test</div>
<nav class="hidden md:block">
<ul class="flex gap-x-4">
<li v-for="item in headerLinks" :key="item.name">
<a :href="item.link" class="transition hover:text-primary">
{{ item.name }}
</a>
</li>
</ul>
</nav>
</div>
</header>
<section class="">
컨텐츠
<!-- Image -->
<div class="col-span-1 flex justify-end">
<img src="~/assets/images/images.webp" class="h-[350px]" />
</div>
</section>
</div>
</template>
</template>
<script setup lang="ts">
const headerLinks = [
{ name: 'Nuxt', link: '#nuxt' },
{ name: 'Basic', link: '#basic' },
{ name: 'Advanced', link: '#advanced' }
]
</script>
Nuxt 3의 assets 디렉토리는 이미지 최적화 및 캐싱 관리의 강력한 도구이며, Tailwind CSS와 결합하여 효율적인 포트폴리오 제작을 가능하게 합니다. 다음 단계에서는 기술 태그를 재사용 가능한 Vue 컴포넌트로 변환하여 코드 유지보수를 용이하게 만듭니다.
중복 코드 문제:
컴포넌트 활용:
재사용 가능한 컴포넌트:
[이전 상태]
스킬 나열
└── <span>Javascript</span>
└── <span>Vue</span>
└── <span>Nuxt</span>
└── <span>Tailwind CSS</span>
↓ 개선
[개선 후]
SkillChip 컴포넌트 도입
├── <skill-chip>Javascript</skill-chip>
├── <skill-chip>Vue</skill-chip>
├── <skill-chip>Nuxt</skill-chip>
└── <skill-chip>Tailwind CSS</skill-chip>
↓ 효과
코드 중복 감소 → 유지보수성 및 확장성 향상
프로젝트 데이터를 컴포넌트에 재사용하기
이미지 관리
ProjectShowcase 컴포넌트 생성
v-for를 이용한 반복 렌더링
확장성 및 유지보수성 향상
[프로젝트 데이터 관리]
app.vue의 projects 배열 정의
├── name, description, image, link 속성 포함
└── 이미지 public 디렉토리에 저장
↓
[ProjectShowcase 컴포넌트]
project prop을 통해 데이터 수신
├── defineProps로 타입 정의
├── 템플릿에서 프로젝트 정보 표시(이미지, 이름, 설명, 링크)
└── 개별 프로젝트 카드 UI 구성
↓
[v-for 반복 렌더링]
app.vue에서
<project-showcase
v-for="project in projects"
:project="project"
/>
↓
[결과]
- 프로젝트별 카드 자동 생성
- 데이터 변경 시 쉽게 반영
- 코드 재사용성 및 유지보수성 향상
<style scoped>
를 사용하면 된다.<style scoped>
로 선언된 스타일은 해당 컴포넌트 요소에만 적용되며, 다른 컴포넌트나 페이지에 영향을 주지 않는다.CurvedDivider
라는 컴포넌트를 만들 경우, 이 컴포넌트 내 <style scoped>
를 사용해 배경, 높이, 하단 곡선 등 특정 스타일을 구현할 수 있다.data-v-xxxxxxx
와 같은 고유 식별자를 통해 컴포넌트 범위 안에서만 적용되는 것을 확인할 수 있다.scoped
속성을 함께 사용할 수 있다.[CurvedDivider 컴포넌트 생성]
│
▼
<template> 내 <div class="divider"/>
│
▼
<style scoped> 선언
│
├── .divider 클래스에 스타일 적용 (배경, 높이)
│
└── .divider::before를 이용해 곡선 하단 구현
│
▼
브라우저 렌더링 시 .divider에 data-v-xxxxxxx 속성 자동 부여
│
▼
해당 스타일은 CurvedDivider 컴포넌트 범위에만 적용
│
▼
외부 컴포넌트에 스타일 누출 없음
Composables 소개
useScrollTo composable 구현
BaseHeader 컴포넌트로 헤더 분리
호출 예시
장점
[composables/useScrollTo.ts 작성]
│
▼
로직 캡슐화 → 특정 ID 요소를 찾아 부드럽게 스크롤
▼ Nuxt auto-import 기능
[BaseHeader.vue에서 호출]
├── headerLinks 배열 정의
├── v-for로 링크 렌더링
└── 클릭 시 useScrollTo(link) 호출 → 해당 섹션으로 스크롤
▼
[Hero 섹션에서도 사용]
└── "View My Work" 버튼 @click="useScrollTo('showcase')"
▼
중앙 집중 로직 관리, 코드 중복 감소, 유지보수성 향상
hook.js:608 [Vue warn]: Hydration node mismatch:
- rendered on server: <header class="bg-secondary text-white p-6 bg-neutral-950" data-v-inspector="components/BaseHeader.vue:10:3">…</header>
- expected on client: div
at <App key=4 >
at <NuxtRoot>
해당 에러는 서버 사이드 렌더링(SSR) 결과와 클라이언트 사이드 렌더링(CSR) 결과가 일치하지 않을 때 발생하는 전형적인 하이드레이션(hydration) 불일치 오류입니다. 즉, 서버에서 렌더링된 DOM 구조와 클라이언트가 예상하는 DOM 구조가 달라 생기는 문제입니다.
현재 코드에서 눈에 띄는 부분은 app.vue 파일의 <template>
태그 구조입니다. 예제 코드상 app.vue
내에 다음과 같은 형태를 볼 수 있습니다.
<template>
<template>
<div class="container mx-auto">
<BaseHeader/>
<CurvedDivider/>
<!-- ... 나머지 섹션들 ... -->
</div>
</template>
</template>
위와 같이 <template>
안에 또 다른 <template>
를 중첩 사용하는 것은 비정상적인 구조입니다.
Nuxt 컴포넌트에서 최상위 <template>
는 단 하나만 존재해야 하고, 그 안에 바로 실제 렌더링될 요소(예: <div>
등)가 위치해야 합니다.
현재 상태에서는 SSR 시에는 <header>
가 상위 엘리먼트로 렌더링되고, 클라이언트는 <div>
를 예상하는 등 불일치가 발생할 수 있습니다.
Tailwind CSS 통합 및 커스터마이징
public/와 assets/ 디렉토리 차이
컴포넌트를 통한 UI 재사용성 강화
<slot>
을 활용해 유연한 컴포넌트 구성 가능. 예를 들어 SkillChip 내부 <slot></slot>
으로 스킬 이름을 부모 컴포넌트에서 전달.Scoped 스타일을 통한 스타일 격리
<style scoped>
를 통해 컴포넌트 내부 스타일이 해당 컴포넌트에만 적용되도록 하여, 다른 컴포넌트와 스타일 충돌을 방지.Composables를 통한 로직 공유
[Tailwind CSS 통합 및 커스터마이징]
│
▼
[레이아웃 및 폰트 설정 → UI 분위기 확립]
│
▼
[public/ vs assets/ 디렉토리 구분으로 자산 관리]
│
▼
[재사용 가능한 컴포넌트 (ProjectShowcase, SkillChip)]
│
▼
<slot> 활용으로 유연한 컴포넌트 구성
│
▼
[<style scoped>로 스타일 격리 및 충돌 방지]
│
▼
[Composables (useScrollTo)로 로직 재사용 및 auto-import]
│
▼
[앞으로 라우팅, 레이아웃, 플러그인 활용 확대]
아래 정리는 Nuxt 3를 활용한 부동산 매물 리스트 웹사이트 프로젝트를 다루는 내용을 정리한 것입니다. 요구사항에 따라 이해하기 쉽게 정리하고, 데이터 흐름을 화살표로 표현한 도식, 그리고 타입스크립트 기반 예시 코드를 포함합니다.
이 장에서는 Nuxt 3를 활용하여 부동산 매물 리스트를 보여주는 프로젝트를 진행한다.
이전 장들에서 Nuxt 3 프로젝트 설정과 Tailwind CSS 통합을 익혔다면, 이제는 이를 바탕으로 한 단계 발전된 기능들을 다룬다.
특히, 레이아웃(Layout), 라우팅(Routing), 플러그인(Plugins) 등의 Nuxt 3 핵심 기능에 초점을 맞춘다.
이번 장에서 다룰 주요 내용은 다음과 같다.
<NuxtPage />
컴포넌트를 통해 하위 페이지를 렌더링한다.[사용자 요청]
↓
↓ (URL 접근 ex: /)
↓
[Nuxt 라우터]
│─→ [layouts/default.vue] 기본 레이아웃 로드
│ ↓
│ ↓ <NuxtPage />를 통해 해당 페이지 컴포넌트 로드
│
│─→ [pages/index.vue] 홈 페이지 데이터 로딩
│ ↓
│ ↓ (동적 라우트 접근 /listing/:id)
│─→ [pages/listing/[id].vue] 매물 상세 정보 요청(API)
│ ↓
│ ↓ 데이터 검증
│ ↓ 유효하지 않을 시 [error.vue]로 이동
│
│─→ [plugins/dayjs.ts] 전역 날짜 처리
│
↓
[렌더링 및 사용자에게 화면 제공]
<script setup lang="ts">
import { ref } from 'vue'
// 예를 들어 부동산 매물 데이터 타입 정의
interface Property {
id: number
title: string
price: number
createdAt: string
}
const properties = ref<Property[]>([])
// 페이지가 로드될 때 API 호출 (가상의 fetch)
onMounted(async () => {
// 실제로는 fetch나 useFetch 훅 사용
const fetchedProperties: Property[] = [
{ id: 1, title: '아름다운 아파트', price: 300000000, createdAt: '2024-01-01' },
{ id: 2, title: '넓은 주택', price: 500000000, createdAt: '2024-02-15' }
]
properties.value = fetchedProperties
})
</script>
<template>
<div class="p-4">
<h1 class="text-2xl font-bold mb-4">메인 페이지</h1>
<ul>
<li v-for="prop in properties" :key="prop.id">
<NuxtLink :to="`/listing/${prop.id}`">
{{ prop.title }} - {{ prop.price }}원
</NuxtLink>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRoute } from '#app'
interface PropertyDetail {
id: number
title: string
price: number
description: string
createdAt: string
}
const route = useRoute()
const property = ref<PropertyDetail | null>(null)
const error = ref<string | null>(null)
onMounted(async () => {
const id = Number(route.params.id)
// 가상의 API 호출
const fetchedProperty: PropertyDetail | null =
id === 1
? { id: 1, title: '아름다운 아파트', price: 300000000, description: '편의시설 인접', createdAt: '2024-01-01' }
: null
if (!fetchedProperty) {
error.value = '매물을 찾을 수 없습니다.'
} else {
property.value = fetchedProperty
}
})
</script>
<template>
<div class="p-4">
<div v-if="error">
<h1 class="text-red-500 text-xl">{{ error }}</h1>
</div>
<div v-else-if="property">
<h1 class="text-2xl font-bold mb-2">{{ property.title }}</h1>
<p>가격: {{ property.price }}원</p>
<p>설명: {{ property.description }}</p>
<p>등록일: {{ $dayjs(property.createdAt).format('YYYY-MM-DD') }}</p>
</div>
<div v-else>
로딩 중...
</div>
</div>
</template>
import { defineNuxtPlugin } from '#app'
import dayjs from 'dayjs'
export default defineNuxtPlugin(() => {
return {
provide: {
dayjs
}
}
})
이렇게 하면 모든 컴포넌트 내에서 this.$dayjs
또는 setup
환경에서는 useNuxtApp().$dayjs
로 dayjs를 활용할 수 있다.
아래는 Nuxt 3의 라우팅, 레이아웃, 플러그인 개념을 재정리한 내용과, 데이터 흐름도, 그리고 타입스크립트 기반의 예시 코드이다.
라우팅(Routing)
레이아웃(Layouts)
definePageMeta
또는 <script>
부분에서 layout
속성을 설정하면 해당 페이지에만 특정 레이아웃을 사용할 수 있다.플러그인(Plugins)
[사용자 요청]
↓ URL 접근 (ex: /listing/1)
↓
[Nuxt 라우터] --(자동 매핑)--> [pages 디렉토리 구조 따른 페이지 판단]
↓
[layouts/default.vue] -- 레이아웃 적용 --> <slot /> 위치에 페이지 내용 렌더
↓
[plugins/*] -- 앱 초기화 시 전역 플러그인 등록
↓
[렌더링 결과 사용자에게 전달]
layouts/default.vue
또는 지정된 레이아웃을 통해 감싸진 후 화면에 렌더링된다.<script setup lang="ts">
const message = ref('메인 페이지입니다!')
const {$axios} = useNuxtApp()
onMounted(async () => {
const data = await $axios.get('/items')
console.log(data)
})
</script>
<template>
<div>
<h1>{{ message }}</h1>
<NuxtLink to="/about">About 페이지로 이동</NuxtLink>
</div>
</template>
<script setup lang="ts">
definePageMeta({
layout: 'custom' // layouts/custom.vue 라우트 사용
})
const info = '이 페이지는 custom 레이아웃을 사용합니다.'
</script>
<template>
<div>
<h2>{{ info }}</h2>
</div>
</template>
import axios from 'axios'
export default defineNuxtPlugin(() => {
const instance = axios.create({
baseURL: 'https://api.example.com'
})
return {
provide: {
axios: instance
}
}
})
<template>
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
</template>
<script setup lang="ts">
</script>
plugins/axios.ts
플러그인은 앱 시작 시 등록되어, useNuxtApp().$axios
를 통해 글로벌하게 Axios 인스턴스에 접근할 수 있다.
아래는 Nuxt 3 프로젝트("Homescape") 초기 설정 및 Tailwind CSS, Google Fonts 통합 과정을 한글로 정리한 내용이다. 또한 데이터 흐름을 도식화하고, 타입스크립트 기반 예시 코드를 포함하였다.
npx nuxi init [--verbose|-v] [--template,-t] [dir]
Tailwind CSS 및 Google Fonts 설치 및 설정
Tailwind CSS 및 @nuxtjs/google-fonts 모듈 설치: pnpm i -D @nuxtjs/tailwindcss @nuxtjs/google-fonts
nuxt.config.ts 파일에 모듈과 Google Fonts 설정 추가:
export default defineNuxtConfig({
devtools: { enabled: true },
modules: [
'@nuxtjs/tailwindcss',
'@nuxtjs/google-fonts'
],
googleFonts: {
families: {
Inter: [100, 300, 400, 700, 900]
}
}
})
이로써 Tailwind CSS 및 Google Fonts 모듈이 Nuxt 앱에 통합되며, 지정한 폰트(Inter)가 자동으로 불러와진다.
Tailwind CSS 설정 파일 생성 및 스타일 커스터마이징
tailwind.config.js 파일 생성 후 컬러, 폰트, 컨테이너 설정:
import colors from 'tailwindcss/colors'
/** @type {import('tailwindcss').Config} */
export default {
content: [],
theme: {
extend: {
colors: {
primary: colors.amber[600],
secondary: colors.slate[100],
red: colors.red[600],
yellow: colors.yellow[500]
},
fontFamily: {
sans: ['Inter', 'sans-serif']
},
container: {
center: true,
padding: {
DEFAULT: '1.5rem',
lg: '4rem',
xl: '4rem',
'2xl': '4rem'
},
screens: {
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
'2xl': '1400px'
}
}
}
},
plugins: []
}
색상, 폰트, 레이아웃 등을 원하는 대로 조정해 Nuxt 앱 전반에 걸쳐 통일된 스타일을 적용한다.
pages/index.vue (tailwind 적용)
<script setup lang="ts">
const message = ref('메인 페이지입니다!')
const {$axios} = useNuxtApp()
onMounted(async () => {
const data = await $axios.get('/items')
console.log(data)
})
</script>
<template>
<div>
<h1>{{ message }}</h1>
<NuxtLink to="/about">About 페이지로 이동</NuxtLink>
<div class="text-8xl text-primary text-center">
Homescape
</div>
</div>
</template>
[명령어 실행: nuxi init] → [Nuxt 3 프로젝트 구조 생성]
↓
[nuxt.config.ts] → (modules 설정) → [@nuxtjs/tailwindcss, @nuxtjs/google-fonts 로드]
↓
[tailwind.config.js] → tailwind 설정 로딩 및 빌드 시 적용
↓
[app.vue] → <template> 내 Tailwind 클래스 사용
↓
[브라우저 출력] → Homescape 텍스트가 스타일 적용되어 표시
아래는 Nuxt 3에서 기본 레이아웃을 정의하고, 404 페이지로 이를 무시(오버라이드)하는 과정을 정리한 내용이다. 요구사항에 따라 이해하기 쉽게 한글로 정리하고, 데이터 흐름도, 타입스크립트 기반 예시 코드를 포함한다.
기본 레이아웃 정의
404 페이지 정의 (오버라이드)
사용자 경험 강화
[사용자 요청: 특정 URL 접근]
↓
↓ (Nuxt 라우터를 통해 페이지 결정)
↓
[layouts/default.vue] - 기본 레이아웃 로딩
↓
<NuxtPage /> - 요청한 페이지를 여기서 렌더링
│
└─→ 만약 존재하지 않는 페이지 요청 → 404 처리
↓
[error/404 페이지 로드]
↓
기본 레이아웃 대신 별도 템플릿으로 사용자 안내
error.vue (루트 폴더에 위치)
<script setup lang="ts">
</script>
<template>
<div class="flex flex-col justify-center items-center min-h-screen bg-neutral">
<h2 class="text-4xl font-bold mb-4 text-red-500">404 - 페이지를 찾을 수 없습니다</h2>
<p class="mb-4">요청하신 페이지가 존재하지 않습니다.</p>
<NuxtLink to="/" class="text-primary underline">홈으로 돌아가기</NuxtLink>
</div>
</template>
아래는 Nuxt 3에서 기본 레이아웃(default.vue)를 정의하고 이를 app.vue에 적용하는 과정을 한글로 정리한 것이다. 또한 데이터 흐름을 도식화하고, 코드 예시를 타입스크립트 기반으로 재작성하였다.
기본 레이아웃 디렉토리 및 파일 생성
layouts/default.vue
파일을 만들고, 해당 파일에 공통적으로 적용될 HTML 구조(헤더, 푸터, 메인 콘텐츠 영역 등)를 정의한다.<slot />
태그는 각 페이지 컴포넌트의 콘텐츠가 삽입되는 자리이다.기본 레이아웃 적용
app.vue
파일에서 <NuxtLayout>
컴포넌트를 사용하여 모든 페이지의 상위에 기본 레이아웃을 적용한다.pages
디렉토리 내 페이지들은 자동으로 layouts/default.vue
에 정의된 레이아웃 구조 안에 렌더링되며, <slot />
위치에 각 페이지별 내용이 들어간다.결과 확인
[사용자 브라우저 요청]
↓
↓ (Nuxt 라우터로 pages 디렉토리 내 해당 페이지 컴포넌트 결정)
↓
[app.vue - <NuxtLayout>] 사용 → [layouts/default.vue] 호출
↓
<slot /> 내에 [pages/...] 파일의 콘텐츠 삽입
↓
[최종 렌더링 결과: 헤더 + 페이지 본문 + 푸터 등의 공통 레이아웃 구조]
layouts/default.vue
<script setup lang="ts">
</script>
<template>
<div class="min-h-screen flex flex-col">
<header class="p-4 bg-gray-200">the header</header>
<main class="flex-1">
<slot />
</main>
<footer class="p-4 bg-gray-100 text-center">
© 2024 Homescape, All rights reserved.
</footer>
</div>
</template>
app.vue
<template>
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
</template>
<script setup lang="ts">
</script>
아래는 Nuxt 3 프로젝트에서 베이스 헤더(BaseHeader), 뉴스레터 섹션(NewslettersSection), 그리고 푸터(Footer) 컴포넌트를 정의하고 이를 기본 레이아웃(default.vue)에 통합하는 과정을 정리한 것이다. 요구사항에 따라 한글로 이해하기 쉽게 정리하고, 데이터 흐름을 도식화했으며, 코드 예시를 타입스크립트를 활용한 형태로 재작성하였다.
베이스 컴포넌트 구조 잡기
Nuxt의 컴포넌트 자동 인식 및 네이밍 규칙
components/base
디렉토리에 Header.vue
를 생성하면 <base-header />
라는 이름으로 어디서든 사용할 수 있다.Header, NewslettersSection, Footer 컴포넌트 구현
기본 레이아웃 default.vue에 통합
default.vue
레이아웃 파일에 <base-header />
, <slot />
, <base-newsletters-section />
, <base-footer />
순으로 배치함으로써 전체 페이지 구조를 확립한다.<slot />
을 통해 각각의 페이지 콘텐츠를 기본 레이아웃 구조 안에 삽입한다.[사용자 요청]
↓ (Nuxt 라우터를 통해 페이지 결정)
[layouts/default.vue] - 기본 레이아웃 불러옴
↓
↓ <base-header /> 상단에 삽입
↓ <slot /> - 페이지별 콘텐츠 삽입
↓ <base-newsletters-section /> 하단 섹션 추가
↓ <base-footer /> 푸터 추가
↓
[최종 렌더링] - 헤더 + 페이지 본문 + 뉴스레터 섹션 + 푸터 구조가 적용된 화면
아래는 Nuxt 3 프로젝트에서 pages 디렉토리를 활용하여 홈 페이지(인덱스 페이지)와 기타 페이지를 정의하고, app.vue를 통해 <NuxtLayout>
와 <NuxtPage>
를 조합하는 방법을 정리한 내용이다.
또한 이해를 돕기 위해 흐름도식과 타입스크립트 기반 예시 코드를 포함했다.
pages 디렉토리의 역할
app.vue를 통한 전역 레이아웃 구조 확립
<NuxtLayout>
를 호출하고 <NuxtPage>
를 배치함으로써 페이지별 콘텐츠가 레이아웃 구조 안에 렌더링되도록 한다.<NuxtLayout>
: 레이아웃을 적용하는 래퍼(wrapper) 역할<NuxtPage>
: 현재 요청된 페이지 컴포넌트를 삽입하는 위치홈 페이지(index.vue) 예시
[사용자가 '/' 경로 요청]
↓
[Nuxt 라우터] - pages 디렉토리 구조 분석 → index.vue 매핑
↓
[app.vue] - <NuxtLayout> 안에 <NuxtPage> 삽입
↓
<NuxtLayout>에서 정의된 기본 레이아웃 구조 불러옴
↓
<NuxtPage> 위치에 pages/index.vue 내용 렌더링
↓
[브라우저]에 레이아웃 + 인덱스 페이지 콘텐츠 표시
app.vue
<template>
<NuxtLayout>
<NuxtPage/>
</NuxtLayout>
</template>
<script setup lang="ts">
</script>
<NuxtPage>
를 통해 현재 URL 경로에 맞추어 로딩된다.
그 위에 <NuxtLayout>
를 사용함으로써 모든 페이지가 같은 레이아웃 구조를 공유할 수 있다.pages/index.vue
<script setup lang="ts">
const welcomeMessage = ref<string>('홈 페이지에 오신 것을 환영합니다!')
</script>
<template>
<div class="container mx-auto py-10 text-center">
<h1 class="text-3xl font-bold">{{ welcomeMessage }}</h1>
<p class="mt-4">이 곳에서 다양한 부동산 매물을 확인할 수 있습니다.</p>
</div>
</template>
아래는 Nuxt 3 프로젝트에서 타입스크립트를 활용해 데이터 타입(Property, Category)을 전역 정의하고, 이를 통해 애플리케이션 전반에서 안정적인 타입 관리를 하는 과정을 정리한 것이다. 또한 데이터 흐름을 도식화하고, 타입스크립트 기반 예시 코드를 제시한다.
타입스크립트 기반 데이터 구조 정의
타입 전역 선언
types/index.ts
// types/index.ts
export {}
declare global {
type Property = {
id: string
title: string
description: string
image: string
category: string // 카테고리 식별용
bedrooms: number
bathrooms: number
squareFeet: number
price: number
listedDate: string
}
type Category = {
id: string
name: string
description: string
image: string
}
}
이로써 모든 컴포넌트, 페이지, 플러그인 등에서 Property와 Category 타입을 바로 사용할 수 있다.
아래는 Nuxt 3 프로젝트에서 컴포넌트 파일 배치 방식과 네이밍에 관한 정리를 한글로 이해하기 쉽게 재구성한 것이다. 또한, 데이터 흐름을 도식화하고, 간단한 타입스크립트 예시 코드를 추가하였다.
컴포넌트 경로 구조
<property-card />
장점
Nuxt의 컴포넌트 자동 로딩
<property-card />
와 같이 바로 템플릿에서 사용할 수 있다.[components 디렉토리 설정]
↓
↓ components/property/card.vue 또는 components/PropertyCard.vue
↓
[Nuxt 자동 컴포넌트 로딩] - 디렉토리 구조 분석 후 컴포넌트 등록
↓
<template> 내에서 <property-card />로 바로 사용 가능
components/property/card.vue
<script setup lang="ts">
// 전역 타입: Property (이미 types/index.ts 등에 선언되어 있다고 가정)
const props = defineProps<{
property: Property
}>()
</script>
<template>
<div class="border p-4 rounded shadow-sm">
<img :src="property.image" :alt="property.title" class="w-full h-48 object-cover mb-2"/>
<h2 class="text-xl font-semibold">{{ property.title }}</h2>
<p>{{ property.description }}</p>
<p>가격: {{ property.price }}원</p>
<p>{{ property.bedrooms }}베드룸 / {{ property.bathrooms }}욕실</p>
</div>
</template>
<style scoped>
/* 필요한 경우 컴포넌트 전용 스타일 정의 */
</style>
<property-card />
로 접근 가능하며, 해당 경로 구조 덕분에 관련 매물 관련 컴포넌트를 한 곳에 모아 관리할 수 있다.아래는 Nuxt 3 프로젝트에서 Property Card 컴포넌트와 Category Card 컴포넌트를 정의하고 이를 활용하는 과정을 한글로 정리한 것이다. 또한, 데이터 흐름을 도식화하고, 타입스크립트 기반 예시 코드를 포함한다.
Property Card 컴포넌트 (/components/property/Card.vue):
Category Card 컴포넌트 (/components/category/Card.vue):
글로벌 타입 재활용:
향후 페이지 활용:
[Property, Category 타입 정의]
↓
[PropertyCard, CategoryCard 컴포넌트 개발]
↓
Prop (Property/Category) 전달 → 각 Card 컴포넌트가 UI 렌더링
↓
[홈 페이지 또는 기타 페이지에서 <property-card /> <category-card /> 사용]
↓
사용자가 카드 클릭 → NuxtLink 통해 상세 페이지로 이동
아래는 Nuxt 3 프로젝트에서 홈 페이지를 구성하는 과정(배경 오버레이 컴포넌트, 히어로 섹션, 추천 매물 섹션, 최근 등록 매물 섹션, 카테고리 섹션)을 이해하기 쉽게 정리한 것이다. 또한 데이터 흐름도를 제시하고, 타입스크립트 기반 예시 코드를 포함한다.
베이스 오버레이 배경 컴포넌트(OverlayBg.vue)
히어로 섹션(HeroSection.vue)
추천 매물(FeaturedProperties.vue)와 최근 등록 매물(RecentlyListedProperties.vue) 섹션
카테고리 섹션(ExploreCategories.vue)
프로젝트 구조 정리
최종 결과
[홈 페이지 요청 (/)]
↓
[app.vue] - <NuxtLayout> + <NuxtPage>
↓
[layouts/default.vue] - 헤더, 푸터, 뉴스레터, slot 등 공통 레이아웃 적용
↓
[pages/index.vue] - 홈 페이지 컴포넌트 렌더링
↓
[components/homepage/*]
├─ HeroSection.vue - Hero UI, <base-overlay-bg>, <base-btn>
├─ FeaturedProperties.vue - featuredProperties 데이터 로딩
├─ RecentlyListedProperties.vue - recentlyListedProperties 데이터 로딩
└─ ExploreCategories.vue - category-card 활용
↓
[property-card / category-card]
↓
[브라우저에 UI 렌더링 완료]
동적 라우팅(Dynamic Routing) 개념
카테고리별 목록 페이지 구현
직관적인 사용자 경험
[사용자가 "/categories/[name]" URL 접근]
↓
[Nuxt 라우터에서 name 파라미터 추출: route.params.name]
↓
[name].vue 스크립트 내:
- properties 데이터 로딩
- categoryName = route.params.name
- filteredProperties = properties.filter(...)
↓
[template 내 v-if/v-else 조건]
↓
filteredProperties가 있으면 → property-card를 반복 렌더링
없으면 → "No properties found" 메시지 + 홈으로 돌아가는 링크
↓
[브라우저 출력: 해당 카테고리의 매물 목록 or 안내 메시지]
pages/categories/[name].vue
<script setup lang="ts">
import properties from '~/data/properties' // Property[] 타입의 데이터
const route = useRoute()
const categoryName = route.params.name as string // 라우트 파라미터: 카테고리명
// 필터링된 매물 목록 계산
const filteredProperties = computed<Property[]>(() =>
properties.filter(item => item.category === categoryName)
)
</script>
<template>
<section class="container py-12">
<h2 class="text-3xl text-center font-bold text-gray-800 mb-6">
Properties in {{ categoryName }}
</h2>
<div v-if="filteredProperties.length" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<property-card
v-for="property in filteredProperties"
:key="property.id"
:property="property"
/>
</div>
<div v-else class="text-center py-12">
<div class="text-5xl font-black mb-2">Oops</div>
<p class="text-gray-700 text-xl mb-8">
No properties found for this category.
</p>
<nuxt-link to="/">
<base-btn>return to homepage</base-btn>
</nuxt-link>
</div>
</section>
</template>
<style scoped></style>
data/properties.ts
export default [
{
id: '1',
title: 'Modern home in city center',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
image: '/images/property-01.jpg',
category: '1',
bedrooms: 3,
bathrooms: 2,
squareFeet: 2000,
price: 250000,
listedDate: '2022-01-01'
},
{
id: '2',
title: 'Family home in suburban area',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
image: '/images/property-02.jpg',
category: '1',
bedrooms: 4,
bathrooms: 3,
squareFeet: 2500,
price: 350000,
listedDate: '2022-01-01'
},
{
id: '3',
title: 'Renovated apartment with a view',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
image: '/images/property-03.jpg',
category: '2',
bedrooms: 2,
bathrooms: 2,
squareFeet: 1500,
price: 200000,
listedDate: '2022-01-01'
},
{
id: '4',
title: 'Country house with spacious garden',
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
image: '/images/property-04.jpg',
category: '1',
bedrooms: 4,
bathrooms: 3,
squareFeet: 3000,
price: 400000,
listedDate: '2022-01-01'
}
]
아래는 Nuxt 3 프로젝트에서 동적 라우트를 활용한 개별 매물 상세 페이지를 구현하고, 유효하지 않은 매물 ID 접근 시 404 에러 페이지로 리다이렉션하는 과정을 정리한 것이다. 또한 데이터 흐름도를 제시하고, 타입스크립트 기반 예시 코드를 포함한다.
동적 라우팅을 통한 매물 상세 페이지 구현
데이터 검증(Validation) 추가
사용자 경험 개선
결과
[사용자가 "/properties/:id" 경로 접근]
↓
[pages/properties/[id].vue] 로드
↓ definePageMeta({
validate: async route => {
!!properties.find(item => item.id === route.params.id)
}
})
↓ 검증:
- 매물이 있으면 true 반환 → 렌더링 진행
- 매물이 없으면 false 반환 → Nuxt가 자동으로 404 에러 페이지로 이동
↓
유효한 매물인 경우:
property = properties.find(item => item.id === route.params.id)
↓
[template에서 property 데이터 출력]
↓
[브라우저에 해당 매물 상세 정보 표시]