본문 바로가기

프로젝트/Chillog (Blog)

블로그 사용성 개선 (ver 1.7.5)

서론

아직도 많이 부족한 블로그지만 조금이라도 나아졌으면 해서 부족한 실력을 쥐어 짜 나름의 큰 업데이트를 해 봤다.

기존의 블로그는 모바일, 데스크탑, 태블릿에서 모두 동일한 인터페이스와 사용성을 가졌었다.
아직도 고민이 많이 필요한 부분이지만 태블릿부터 대화면의 장점을 충분히 활용하지 못하는 비효율적인 인터페이스를 가지고 있다고 생각해 조금 바꿔 보기로 했다.

 

본론

Cols, Row의 이해

이미 해당 부분은 갤러리 게시판의 양식을 만들기 위해 진행했던 부분이지만
역시나 따라만 했을 뿐 정확히 이해하지 못했던 것 같다. 이걸 이해하는데 한참이나 걸려버렸다.

<template>
  <v-container fluid>
    <v-row dense>
      <template v-for="item in items">
        <v-col cols="6" sm="3" md="2" :key="item.id">
          <v-card outlined color="" height="100%" :to="toPath(item)">
            <v-img
              :src="isGif(item.images[0].id) ? item.images[0].url : item.images[0].thumbUrl"
              :aspect-ratio="1"
              class="align-end"
            >
              <v-card-actions class="mt-auto">
                <v-spacer/>
                <v-btn
                  small dark
                  color="primary"
                  pointer-events="none"
                ><v-icon left
                :color="liked(item) ? 'info': ''"
                >mdi-heart</v-icon>
                  <span>{{item.likeCount > 9 ? '9+' : item.likeCount}}</span>
                </v-btn>
              </v-card-actions>
            </v-img>
            <v-card-subtitle class="text--primary align-center">
              <v-icon small color="accent" left v-if="newCheck(item.updatedAt, 'hours', 1)">mdi-fire</v-icon>
              <span>{{item.title}}</span>
            </v-card-subtitle>
          </v-card>
        </v-col>
      </template>
    </v-row>
  </v-container>
</template>

위의 코드는 갤러리 게시판의 탬플릿 코드이다.

바둑판식으로 배열하고, 화면 크기에 따라 자연스럽게 표시하는 컨텐츠의 수가 달라진다.
위의 코드를 최대한 단순하게 표현하자면 다음과 같이 바꿀 수 있다.

<template>
    <v-container fluid>
        <v-row dense>
            <v-col cols="12">
            </v-col>
        </v-row>
    </v-container>
</template>

vuetify의 grid 시스템은 다음의 링크에서 자세히 확인할 수 있다.
(https://vuetifyjs.com/en/components/grids/#usage)

간단히 말 하자면 유동적인 grid를 구성하기 위해서는 v-container, v-row, v-col 세개가 반드시 필요하다.

v-container의 fluid 파라미터는 크기에 따라 유동적으로 배치할 수 있도록 하며,
v-row는 행을 담당하며, dense 파라미터는 행 간의 간격에 관여한다. dense 대신 no-gutter로 여백을 완전히 지우는 것도 가능하다.
v-col은 열을 담당한다. col은 12개의 보이지 않는 칸을 가지고 있으며, 몇 칸은 사용할 건지는 cols 파라미터로 정한다.

위의 개념을 이해하지 못해 모바일과 데스크탑을 다르게 구현하는데 애를 좀 먹었다.

화면 크기 분기

v-col이 가진 여러 파라미터중 화면크기에 대응하는 파라미터가 있어 크게 어렵지 않다.

타겟에 따라 골라서 사용하면 된다.
나름 세세하게 분리 해 놨지만 현행 모바일기기들의 방향성을 보면

핸트폰 = xs
태블릿, 데스크탑 = sm
정도로 생각하면 편하다.

해당되는 화면 크기에는 cols파라미터에서 설정한 만큼의 칸을 몇 개나 사용할 것인지에 대한 것으로
cols에서 파라미터만큼을 나눈 몫 만큼 컨텐츠가 배치된다고 생각하면 된다.
예를 들면 cols가 12고 lg가 6이라면 두 개가 들어가게 된다.

따라서 구조를 다음과 같이 바꿨다.

<template>
    <v-container fluid>
        <v-row dense>
            <v-col cols="12" sm="6">
            </v-col>
        </v-row>
    </v-container>
</template>

이렇게 되면 가로가 600이상인 환경에선 컨텐츠가 두개로 표시된다.

변수

본 블로그에는 일반 글 뿐만이 아닌 중요와 공지라는 두개의 카테고리가 존재한다.
해당 카테고리는 글의 작성 시점과 무관하게 최상위에 위치하도록 하고 있다.
또한 내용을 제외한 제목과 조회수 같은 약간의 카운팅 정보만 제공하고 있다.
따라서 해당 글들은 굳이 분할해서 보여주는 대신 현행대로 큼지막하게 유지하는 것으로 택했다.

<template v-if="item.important">
  <v-col cols="12" :key="item.important.id">
    <v-card :key="item.id" :to="category ? `${boardId}/${item.id}?category=${category}` : `${boardId}/${item.id}`">
      <v-card-subtitle class="text--primary body-1" :class="item.important > 0 ? 'text-truncate': ''">
        <display-title :item="item"/>
        <v-spacer/>
        <display-count :item="item" :column="false"></display-count>
      </v-card-subtitle>
    </v-card>
  </v-col>
</template>

item의 important 속성으로 template을 분기시켜 줬다.
이제 important 플래그가 생기는 중요, 공지 글들은 그리드배열이 아닌 위에서 정의한 단일 사이즈 배열을 따른다.

 

결론

<template>
  <v-container fluid>
    <v-row dense>
      <template v-for="(item, i) in items">
        <template v-if="item.important">
          <v-col cols="12" :key="item.important.id">
            <v-card :key="item.id" :to="category ? `${boardId}/${item.id}?category=${category}` : `${boardId}/${item.id}`">
              <v-card-subtitle class="text--primary body-1" :class="item.important > 0 ? 'text-truncate': ''">
                <display-title :item="item"/>
                <v-spacer/>
                <display-count :item="item" :column="false"></display-count>
              </v-card-subtitle>
            </v-card>
          </v-col>
        </template>
        <template v-if="!item.important">
          <v-col cols="12" sm="6" :key="item.id">
            <v-card :key="item.id" :class="$vuetify.breakpoint.xs ? '' : 'ma-4'" :flat="$vuetify.breakpoint.xs">
              <v-card color="transparent" flat :to="category ? `${boardId}/${item.id}?category=${category}` : `${boardId}/${item.id}`">
                  <template>
                    <v-card-subtitle class="text--primary body-1" :class="item.important > 0 ? 'text-truncate': ''">
                      <display-title :item="item"/>
                    </v-card-subtitle>
                      <v-card-text>
                          <viewer class="tui-dark" v-if="item.summary" :initialValue="item.summary" @load="onViewerLoad" :options="tuiOptions"></viewer>
                          <v-container v-else>
                              <v-row justify="center" align="center">
                                  <v-progress-circular indeterminate></v-progress-circular>
                              </v-row>
                          </v-container>
                      </v-card-text>
                      <v-card-actions class="d-flex justify-center">
                          <v-btn text color="default" class="mb-4">
                              <v-icon left>mdi-dots-vertical</v-icon>
                          </v-btn>
                      </v-card-actions>
                  </template>
              </v-card>
              <template v-if="!item.important">
                  <v-card-actions>
                      <span class="font-weight-black caption ml-3"><display-time :time="item.createdAt"></display-time></span>
                      <v-spacer/>
                      <display-user :user="item.user"></display-user>
                  </v-card-actions>
                  <v-card-actions>
                      <v-spacer/>
                      <display-count :item="item" :column="false"></display-count>
                  </v-card-actions>
                  <v-card-text>
                      <v-row justify="start" align="center" class="px-4">
                          <v-btn
                              color="info"
                              depressed
                              small
                              outlined
                              class="mr-4"
                              :to="`${$route.path}?category=${item.category}`"
                              width="100"
                          >
                          {{item.category}}
                              <v-icon right>mdi-menu-right</v-icon>
                          </v-btn>
                          <v-chip small label outlined color="default" class="mt-2 mr-2 mb-2" v-for="tag in item.tags" :key="tag" v-text="tag"></v-chip>
                      </v-row>
                  </v-card-text>
                </template>
            </v-card>
            <v-divider v-if="i < items.length -1 && $vuetify.breakpoint.xs" :key="i"/>
          </v-col>
        </template>
      </template>
    </v-row>
  </v-container>
</template>

최종적으로 완성 된 코드이다.

폰과 태블릿 환경에서 서로 다른 레이아웃을 보여주는 것을 확인 할 수 있다.

혼자 바닥부터 만들었으면 지금처럼 디자인을 손 보는 것으로는 안 끝났을 작업이라고 생각한다.
여러가지 변수를 생각해 꾸준히 모듈화 해 주신 fkkmemi님께 감사를 보낸다.


Log

2021.07.23.
블로그 이전으로 인한 글 옮김 및 수정.