결과 화면

 

1. RouterName 변수 만들기

  • configs/router.js
  1. RouterName 변수(객체)를 만든다.
  2. 라우터 변수의 이름과 경로를 정의한다. (경로는 pages 폴더에 만든 .vue 파일의 경로와 동일하게 만든다.)

RouterName 변수 파일을 따로 만드는 이유는 피씨 메뉴, 모바일 메뉴, 사이트맵 등 여러 곳에서 사용했을 경우, 이름 또는 경로가 변경되면 여러 번 수정해야 하기 때문에 router 파일을 하나 만들어 import 시켜서 사용하면 한 번만 수정하면 된다.

export const RouterName = {
  MAIN: '/',
  SUB: '/sub',

  AUTH_LOGIN: '/auth/login'
}

 

2. TheHeader 컴포넌트 만들기

  • components/layout/TheHeader.vue
  1. <script> 태그에 { RouterName }을  import 시킨다.
  2. <script> 태그의 data() 영역에 navigation 변수(배열)를 만든다.
  3. name은 네비게이션 이름, to는 RouterName에 작성한 라우터 변수를 작성한다.
  4. <nav> 태그에 v-for를 사용해 네비게이션을 만든다.
<template>
  <header class="wrap">
    <nav>
      <NuxtLink
        v-for="(nav, key) in navigation"
        :key="`nav-${key}`"
        :to="{ path: `${nav.to}`}">
        {{ nav.name }}
      </NuxtLink>
    </nav>
  </header>
</template>

<script>
import { RouterName } from '@/configs/router'

export default {
  name: 'TheHeader',

  data() {
    return {
      navigation: [
        {
          name: '메인',
          to: RouterName.MAIN
        },
        {
          name: '서브',
          to: RouterName.SUB
        },
        {
          name: '로그인',
          to: RouterName.AUTH_LOGIN
        }
      ]
    }
  }
}

<style lang="scss" scoped>
header {
  ...
}
</style>

 

3. TheHeader 컴포넌트 import 시키기

  • components/layout/TheLayout.vue
<template>
  <div>
    <TheHeader/>

    <main class="wrap">
      <slot/>
    </main>

    <footer></footer>
  </div>
</template>

<script>
import TheHeader from '@/components/layout/TheHeader'

export default {
  name: 'TheLayout',

  components: {
    TheHeader
  }
}
</script>

 

  • pages/index.vue
<template>
  <TheLayout>
    <h1>메인 페이지</h1>
  </TheLayout>
</template>

<script>
import TheLayout from '@/components/layout/TheLayout'

export default {
  name: 'Main',

  components: {
    TheLayout
  }
}
</script>

grid-column, grid-row

grid-column 속성은 그리드 라인 숫자에 맞춰 시작 열과 끝 열을 지정할 수 있다.

grid-row 속성은 그리드 라인 숫자에 맞춰 시작 행과 끝 행을 지정할 수 있다.

 

결과 화면

  • 피씨

 

  • 모바일

 

  • CSS / SCSS

그리드 레이아웃 안에 또 그리드를 사용해도 된다.

하지만 display: grid; 만 사용하면 원하는 영역만큼 공간을 차지하지 않기 때문에 grid-column, grid-row를 함께 사용한다.

 

귤 영역: grid-column: 1 / 3;

열의 1번째 라인부터 3번째 라인까지 영역을 차지한다.

 

귤 영역: grid-column: 1/2; grid-row: 1/3;

열의 1번째 라인부터 2번째 라인까지 영역을 차지한다.

행의 1번째 라인부터 3번째 라인까지 영역을 차지한다.

 

오렌지 영역: grid-column: 2/3;

열의 2번째 라인부터 3번째 라인까지 영역을 차지한다.

 

레몬 영역: grid-column: 2/3;

열의 2번째 라인부터 3번째 라인까지 영역을 차지한다.

 

.chart {
  ul {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    width: 100%;

    li {
      ...
    }
  }

  // 첫번째 차트 (회색)
  .first {
    background-color: #999;
  }

  // 두번째 차트 (진한 회색)
  .second {
    ul {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      grid-template-rows: 100px 100px;
      gap: 20px;

      li {
        background-color: #333;

        &:nth-child(1) {
          grid-column: 1 / 3;
        }
      }
    }
  }

  // 세번째 차트 (빨강)
  .third {
    ul {
      display: grid;
      grid-template-columns: 1fr;
      grid-template-rows: 100px 100px;
      gap: 20px;

      li {
        background-color: #d90000;
      }
    }
  }
}

@media screen and (max-width: 1024px) {
  .chart {
    ul {
      grid-template-columns: repeat(1, 1fr);

      li {
        ...
      }
    }

    // 첫번째 차트 (회색)
    .first {
      height: 100px;
    }

    // 두번째 차트 (진한 회색)
    .second {
      ul {
        grid-template-columns: repeat(2, 1fr);
        grid-template-rows: repeat(2, 100px);

        li {
          &:nth-child(1) {
            grid-column: 1 / 2;
            grid-row: 1 / 3;
          }

          &:nth-child(2) {
            grid-column: 2 / 3;
          }

          &:nth-child(3) {
            grid-column: 2 / 3;
          }
        }
      }
    }

    // 세번째 차트 (빨강)
    .third {
      ul {
        grid-template-columns: repeat(2, 1fr);
        grid-template-rows: repeat(1, 100px);
      }
    }
  }
}

CSS Grid

그리드 레이아웃은 웹페이지를 위한 2차원 레이아웃 시스템이다.

행(row, )과 열(column, )에 콘텐츠를 배치할 수 있고, 쉽게 말해 격자무늬로 생긴 레이아웃을 만들 때 그리드를 사용하면 편리하게 작업할 수 있다.

 

 

결과 화면

3 x 3 레이아웃 만들기

 

1. grid 선언

  • HTML

HTML 코드는 계속 동일하다.

<ul class="grid">
  <li>산</li>
  <li>바다</li>
  <li>구름</li>
  <li>자전거</li>
  <li>자동차</li>
  <li>비행기</li>
  <li>사자</li>
  <li>호랑이</li>
  <li>곰</li>
</ul>

 

  • CSS / SCSS

display: grid; 를 선언하면 위의 이미지처럼 격자무늬로 레이아웃이 나뉜 것을 확인할 수 있다.

.grid {
  display: grid;
}

 

2. rows와 columns를 이용해 행과 열의 모양 만들기

  • CSS / SCSS

grid-template-columns열(column, )을 개수는 정할 때 사용한다.

gtid-template-rows행(row, )의 높이를 정할 때 사용한다.

 

보통 그리드 레이아웃은 반응형 웹 사이트에서 많이 사용하기 때문에 columns는 1fr 단위를 많이 사용하고,

rows는 고정 높이 값이 아니라면 콘텐츠 높이만큼 영역을 차지하기 때문에 rows는 값을 지정하지 않는다. 

 

fr은 유연한 크기를 가지는 단위이다.

그리드 컨테이너 내의 공간 비율을 분수(fraction)로 나타낸다.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr); // = grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: repeat(3, 100px); // = grid-template-rows: 100px 100px 100px;

  // 레이아웃 구별을 위해 색상 CSS를 추가했다.
  li {
    padding: 20px;

    &:nth-child(1),
    &:nth-child(2),
    &:nth-child(3) {
      background-color: blanchedalmond;
    }

    &:nth-child(4),
    &:nth-child(5),
    &:nth-child(6) {
      background-color: darkseagreen;
    }

    &:nth-child(7),
    &:nth-child(8),
    &:nth-child(9) {
      background-color: cadetblue;
    }
  }
}

 

3. gap을 이용해서 행과 열에 간격 맞추기

  • CSS / SCSS

column-gap 은 열과 열 사이의 간격을 지정한다.

row-gap 은 행과 행 사이의 간격을 지정한다.

 

gap 은 행과 열을 동일한 간격으로 지정한다.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 100px);
  column-gap: 20px;
  row-gap: 50px;

  li {
    ...
  }
}

 

4. flex를 사용해 컨텐츠 가운데 정렬

  • CSS / SCSS

gird와 flex를 함께 사용하면 보다 쉽게 레이아웃을 만들 수 있다.

 

gird는 컨테이너 영역의 레이아웃을 만들 때 사용하면 좋고,

flex는 컨테이너 영역 안의 콘텐츠를 정렬(왼쪽 정렬, 가운데 정렬, 오른쪽 정렬)할 때 사용하면 좋다.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 100px);
  gap: 20px;

  li {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 20px;
    border-radius: 10px;

    &:nth-child(1),
    &:nth-child(2),
    &:nth-child(3) {
      background-color: blanchedalmond;
    }

    &:nth-child(4),
    &:nth-child(5),
    &:nth-child(6) {
      background-color: darkseagreen;
    }

    &:nth-child(7),
    &:nth-child(8),
    &:nth-child(9) {
      background-color: cadetblue;
    }
  }
}

스켈레톤 UI (skeleton UI)

스켈레톤 UI는 실제 데이터가 렌더링 되기 전에 보이는 화면의 윤곽을 표현한 로딩 애니메이션이다.

정적인 화면을 보여주는 대신 스켈레톤 애니메이션을 화면에 보여줌으로써 사용자의 요청이 진행 중임을 표시한다.

또한 정적인 화면보다 동적인 화면을 보여주었을 때 로딩 효과를 잘 표현할 수 있어 움직이는 그라데이션 효과를 자주 사용한다.

 

결과 화면

사진이어서 움직이지 않지만, keyframes과 animation을 사용해서 좌에서 우로 그라데이션 효과가 움직인다.

 

  • HTML
<div class="gradient"></div>

 

  • css/scss

색상과 크기는 본인이 원하는 스타일로 작성하면 된다.

keyframes 효과를 다르게 주면 여러 움직임으로 표현할 수 있다.

@keyframes shimmer {
  100% {
    transform: translateX(100%);
  }
}

.gradient {
  position: relative;
  overflow: hidden;
  width: 300px;
  height: 300px;
  background-color: #555;
  border-radius: 16px;

  &::after {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, rgba(#dfdfdf, 0) 0, rgba(#dfdfdf, 0.2) 20%, rgba(#dfdfdf, 0.5) 60%, rgba(#dfdfdf, 0));
    animation: shimmer 5s infinite;
    content: '';
  }
}

v-if

v-if 디렉티비는 조건부로 블록을 렌더링 하고, 블록은 디렉티브 표현식이 ture 일 때만 렌더링 된다.

 

v-else

v-if에 대한 else 블록을 렌더링 하고, 블록은 디렉티브 표현식이 false 일 때만 렌더링 된다.

 

결과 화면

 

  • pages/index.vue

실제 프로젝트를 진행할 때에는 데이터(게시판, 공지사항 등)는 API로 받게 된다. 

데이터가 아무것도 없을 때에도 레이아웃을 보여주어야 하기 때문에 v-if와 v-else를 사용해 렌더링을 한다.

 

v-if 조건문에 맞춰 dataList 배열의 길이가 0이면 <h2> 태그를 렌더링 하고,

dataList 배열의 길이가 0이 아니라면 <ul> 태그를 렌더링 한다.

<template>
  <TheLayout>
    <h2 v-if="dataList.length === 0">데이터가 없습니다.</h2>

    <ul v-else>
      <li
        v-for="(list, key) in dataList"
        :key="`list-${key}`">
        <h2>{{ list.title }}</h2>
        <p>{{ list.content }}</p>
        <span>{{ list.date }}</span>
      </li>
    </ul>
  </TheLayout>
</template>

<script>
import TheLayout from "@/components/layout/TheLayout"

export default {
  name: 'Main',

  components: {
    TheLayout
  },

  data() {
    return {
      dataList: []
    }
  }
}
</script>

v-for

게시판의 게시글 리스트나 반복적인 내용을 보여줘야 할 때 v-for 디렉티브를 사용한다.

v-for="item in items":key 형태이다.

뷰에서 사용하는 for문이라고 생각하면 된다.

 

결과 화면

 

v-for 사용하기

  • pages/index.vue
  1. <script> 태그 안 data() 영역에 배열을 만든다. (dataList)
  2. <li> 태그가 반복되어야 하기 때문에 <li> 태그에 v-for와 :key를 작성한다.
  3. 중괄호 {{ ~ }} 안에 배열의 속성값을 작성한다.
<template>
  <TheLayout>
    <ul>
      <li
        v-for="(list, key) in dataList"
        :key="`list-${key}`">
        <h2>{{ list.title }}</h2>
        <p>{{ list.content }}</p>
        <span>{{ list.date }}</span>
      </li>
    </ul>
  </TheLayout>
</template>

<script>
import TheLayout from "@/components/layout/TheLayout"

export default {
  name: 'Main',

  components: {
    TheLayout
  },

  data() {
    return {
      dataList: [
        {
          title: 'Nuxt.js',
          content: 'Nuxt.js란, Vue.js 애플리케이션을 구축할 수 있는 오픈 소스 프레임워크이다.',
          date: '2023-10-01'
        },
        {
          title: 'Slot',
          content: '컴포넌트를 랜더링 할 때 html로 작성한 코드가 컴포넌트의 slot 부분으로 교체된다.',
          date: '2023-10-02'
        },
        {
          title: '뷰 SASS/SCSS 적용하기',
          content: 'sass/scss를 적용하고 싶은 .vue 파일에서 script 태그 아래에 style 태그를 작성한다.',
          date: '2023-10-03'
        }
      ]
    }
  }
}
</script>

props

props는 부모 컴포넌트의 데이터를 자식 컴포넌트로 전달한다.

부모 > 자식

 

emit

자식 컴포넌트에서 $emit 이벤트를 이용해 부모 컴포넌트에서 만들어 놓은 이벤트를 호출한다.

자식 > 부모

 

BaseButton 만들기

  1. BaseButton.vue 파일을 만들어 버튼을 공통으로 사용한다.
  2. class 속성을 props로 전달받아 css를 적용한다.
  3. $emit를 사용해 부모 컴포넌트에 작성한 이벤트를 호출한다.

 

결과 화면

 

1. BaseButton 컴포넌트 만들기

  • components/base/BaseButton.vue (자식 컴포넌트)
  1. :class="[color]" color 라는 속성을 만들어 부모 컴포넌트에서 색상 값을 전달받는다. (배열 형태로 만들면 여러 가지 값을 전달받을 수 있다.)
  2. <script> 태그 안 props 부분에 color에 대한 타입을 정의한다. (타입을 정의하지 않으면 잘못된 값을 전달받았을 때 콘솔 오류가 발생한다.)
  3. 전달받은 color에 대한 css를 작성한다. (.red, .blue)
  4. @click="$emit('click')" $emit을 이용해 클릭 이벤트를 호출한다. (클릭 이벤트는 부모 컴포넌트에 만든다.)
<template>
  <button
    type="button"
    :class="[color]"
    @click="$emit('click')">
    <slot/>
  </button>
</template>

<script>
export default {
  name: 'BaseButton',

  props: {
    color: {
      type: String,
      required: false
    }
  }
}
</script>

<style lang="scss" scoped>
button {
  width: 150px;
  height: 40px;

  &.red {
    background-color: red;
  }

  &.blue {
    background-color: blue;
  }
}
</style>

 

2. BaseButton 컴포넌트 사용하기

  • pages/index.vue (부모 컴포넌트)
  1. <BaseButton> 컴포넌트를 import 한다.
  2. <BaseButton> 컴포넌트에 color 속성을 작성한다. (color="red", color="blue")
  3. @click 메서드를 통해 클릭 이벤트를 호출한다.
  4. <script> 태그 안 methods 부분에 클릭 이벤트를 작성한다. (redAlert(), blueAlert())
<template>
  <TheLayout>
    <BaseButton
      color="red"
      @click="redAlert">
      빨간색 버튼
    </BaseButton>

    <BaseButton
      color="blue"
      @click="blueAlert">
      파란색 버튼
    </BaseButton>
  </TheLayout>
</template>

<script>
import TheLayout from "@/components/layout/TheLayout"
import BaseButton from "@/components/base/BaseButton"

export default {
  name: 'Main',

  components: {
    TheLayout,
    BaseButton
  },

  methods: {
    redAlert() {
      alert('빨간색 버튼')
    },

    blueAlert() {
      alert('파란색 버튼')
    }
  }
}
</script>

1. sass, sass-loader 설치하기

터미널에서 아래 명령어를 실행한다.

package.json 파일에서 설치된 버전을 확인할 수 있다.

yarn add sass sass-loader

또는

npm install sass sass-loader

 

 

2. style 작성하기

  • pages/index.vue

sass/scss를 적용하고 싶은 .vue 파일에서 <script> 태그 아래에 <style> 태그를 작성한다.

<template>
  <TheLayout>
    <h1>메인 페이지</h1>
  </TheLayout>
</template>

<script>
import TheLayout from "@/components/layout/TheLayout"

export default {
  name: 'Main',

  components: {
    TheLayout
  }
}
</script>

<style lang="scss" scoped>
  h1 {
    color: blue
  }
</style>

 

3. scss 적용이 안됐을 경우

버전 차이로 인해 sass-loader가 작동을 안 할 때가 있다.

sass-loader를 삭제 후, 버전 10을 설치한다.

// 삭제하기
yarn remove sass-loader

// 버전 10 설치하기
yarn add sass-loader@10

 

4. reset.scss 등 전역 scss 적용하기

  • static/css/common.scss

reset, font, layout 등 공통으로 적용하고 싶은 scss 파일이 있다면 매번 스타일을 작성하거나 import 시키는 것보다 전역에서 적용하는 것이 효율적이다.

위의 경로에 common.scss 파일을 만들고, 공통으로 적용하고 싶은 scss 파일을 import 시킨다.

// base
@import './base/reset';
@import './base/font';

// common
@import './common/layout';
@import './common/transition';

...

 

5. nuxt.config.js

nuxt.config.js 파일을 열고, css 부분에 common.scss 파일을 작성한다.

common.scss에 import 시켜둔 모든 scss 파일이 전역에서 적용된 것을 확인할 수 있다.

export default {
  ...
  
  // Global CSS: https://go.nuxtjs.dev/config-css
  css: [
    '@/static/css/common.scss'
  ],
  
  ...
}

 

6. 결과 화면

slot, 슬롯이란

컴포넌트를 랜더링 할 때 html로 작성한 코드가 컴포넌트의 <slot></slot> 부분으로 교체된다.

 

1. 레이아웃 컴포넌트 만들기

  • components/layout/TheLayout.vue

<header>, <footer>는 공통으로 사용하고, <main> 영역만 변경하고 싶어 <slot>을 사용했다.

<template>
  <div>
    <header>헤더</header>

    <main>
      <slot/>
    </main>

    <footer>푸터</footer>
  </div>
</template>

<script>
export default {
  name: 'TheLayout'
}
</script>

 

2. 레이아웃 컴포넌트 import 시키기

  • pages/index.vue

<h1> 이라고 작성한 부분이 <TheLayout> 컴포넌트의 <slot> 영역으로 교체된다.

<template>
  <TheLayout>
    <h1>메인 페이지</h1>
  </TheLayout>
</template>

<script>
import TheLayout from "@/components/layout/TheLayout"

export default {
  name: 'Main',

  components: {
    TheLayout
  }
}
</script>

 

3. 결과 화면

Nuxt.js란

Nuxt.js란, Vue.js 애플리케이션을 구축할 수 있는 오픈 소스 프레임워크이다.

Nuxt.js 공식 문서: https://nuxt.com/

  • 코드 분할
  • SSR, SPA 등 렌더링 결정 가능
  • 서버 측 렌더링
  • 최적화된 이미지
  • SEO

 


1. npm init nuxt-app

터미널에서 아래 명령어를 실행하고, 나오는 옵션들을 선택한다.

프로젝트 이름, Javascript/Typescript 언어 선택 등을 설정할 수 있다.

잘 모르겠으면 전부 앤터를 누른다.

npm init nuxt-app 프로젝트 이름

 

 

2. 로컬호스트 연결 확인하기

설치한 프로젝트로 이동 또는 프로젝트를 열고, 터미널에서 아래 명령어를 실행한다.

yarn dev

또는

yarn build
yarn start

 

 

3. 결과 화면

로컬 호스트 접속 후, 아래 화면이 나온다면 로컬호스트 연결에 성공한 것이다.

 

 

4. 폴더 구조

+ Recent posts