Vue.js 3 & NodeJS/Vue 3

Vue CLI SNS처럼 글 작성 시간 표시(지난 시간 계산) - Vue CLI SNS DateFormat

carrotweb 2022. 5. 29. 16:33
728x90
반응형

게시물의 작성 날짜를 SNS처럼 현재 날짜를 기준으로 작성 날짜를 지난 시간으로 계산하여 표시되도록 해보겠습니다.

 

작성 날짜에 대한 지난 시간 계산을 Vue 컴포넌트의 <script>가 아닌 별도의 스크립트 파일을 생성하고 가져와서 사용하도록 하겠습니다. 이전 "Vue CLI 외부 스크립트(js) 파일 생성 및 사용(가져오기) - Vue CLI Script Export, Import"(https://carrotweb.tistory.com/212)를 참고하시길 바랍니다.

 

 

DateFormat Script 생성

 

1. 스크립트 파일 생성을 위해 "C:\workspaces\nodeserver\testvue\src"에 "commons"폴더를 생성합니다. 그리고 dateformat.js 파일을 생성합니다.

 

2. dateformat.js 파일을 오픈하여  Vue 컴포넌트에서 사용할 수 있게 export default로 처리합니다.

const dateformat = {
};

export default dateformat;

 

​스크립트 함수는 "JavaScript Date - getTime()를 이용하여 날짜 계산, SNS처럼 글 작성 시간 표시 - 지난 시간 계산"(https://carrotweb.tistory.com/163)에서 작성한 함수(elapsedText)를 복사하여 사용할 겁니다. 함수 설명은 이전 내용을 참고하시기 바랍니다.

 

elapsedText에서 SimpleDateTimeFormat() 함수를 사용하기 위해서 this를 추가해 줘야 합니다. 그리고 작성 날짜가 1초만 지나면 "방금 전"이라는 문구는 볼 수 없기 때문에 작성 후 10초까지는 "방금 전"으로 표시되게 변경했습니다.

const dateformat = {
	SimpleDateTimeFormat: function(date, pattern) {
		var days = ["일", "월", "화", "수", "목", "금", "토"];
		var dateString = pattern.replace(/(yyyy|MM|M|dd|d|HH|H|hh|h|mm|m|ss|s|SSS|a|EEE|eee)/g, function(match) {
			var matchString = "";
			switch(match) {
				case "yyyy":
					matchString = date.getFullYear();
					break;
				case "MM":
				case "M":
					matchString = date.getMonth() + 1;
					break;
				case "dd":
				case "d":
					matchString = date.getDate();
					break;
				case "HH":
				case "H":
					matchString = date.getHours();
					break;
				case "hh":
				case "h":
					var hours = date.getHours() % 12;
					matchString = (hours) ? hours : 12;
					break;
				case "mm":
				case "m":
					matchString = date.getMinutes();
					break;
				case "ss":
				case "s":
					matchString = date.getSeconds();
					break;
				case "SSS":
					matchString = date.getMilliseconds();
					break;
				case "a":
					matchString = (date.getHours() < 12) ? "오전" : "오후";
					break;
				case "EEE":
					matchString = days[date.getDay()] + "요일";
					break;
				case "eee":
					matchString = days[date.getDay()];
					break;
				default :
					matchString = match;
					break;
			}
			if (match == "SSS") {
				if (matchString < 10) {
					matchString = "00" + matchString;
				} else if (matchString < 100) {
					matchString = "0" + matchString;
				}
			} else {
				if (match != "M" && match != "d" && match != "H" && match != "h" && match != "m" && match != "s"
					&& match != "a" && match != "EEE" && match != "eee") {
					if ((typeof(matchString) == "number" && matchString < 10)) {
						matchString = "0" + matchString;
					}
				}
			}
			return matchString;
		});
	
		return dateString;
	},
	elapsedText: function(date) {
		// 초 (밀리초)
		const seconds = 1;
		// 분
		const minute = seconds * 60;
		// 시
		const hour = minute * 60;
		// 일
		const day = hour * 24;
		
		var today = new Date();
		var elapsedTime = Math.trunc((today.getTime() - date.getTime()) / 1000);
		
		var elapsedText = "";
		if (elapsedTime < (seconds + 10)) {
			elapsedText = "방금 전";
		} else if (elapsedTime < minute) {
			elapsedText = elapsedTime + "초 전";
		} else if (elapsedTime < hour) {
			elapsedText = Math.trunc(elapsedTime / minute) + "분 전";
		} else if (elapsedTime < day) {
			elapsedText = Math.trunc(elapsedTime / hour) + "시간 전";
		} else if (elapsedTime < (day * 15)) {
			elapsedText = Math.trunc(elapsedTime / day) + "일 전";
		} else {
			elapsedText = this.SimpleDateTimeFormat(date, "yyyy.M.d");
		}
		
		return elapsedText;
	}
};

export default dateformat;

 

 

DateFormat 스크립트 파일을 import로 가져와 적용하겠습니다.

 

 

작성 날짜 SNS처럼 표시하기

 

1. C:\workspaces\nodeserver\testvue\src\views\Home.vue 파일을 오픈합니다. <script>에 dateformat.js 파일을 import로 가져옵니다.

<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue';
import * as bootstrap from 'bootstrap';
import $ from 'jquery';
import dateformat from '@/commons/dateformat.js'; --> 추가

 

2. <script>의 methods에 elapsedText() 메서드를 추가하여 dateformat의 elapsedText() 메서드가 처리되게 합니다.

elapsedText(date) {
	return dateformat.elapsedText(new Date(date));
}

 

3. 리스트에서 작성 날짜에 elapsedText() 메서드를 추가합니다. 그리고 작성자 아이디 대신 닉네임이 표시되도록 수정합니다.

// 변경 전
<span>{{boardItem.writer}}</span>
<small>{{boardItem.writedate}}</small>
// 변경 후
<span>{{boardItem.writerNickname}}</span>
<small>{{elapsedText(boardItem.writedate)}}</small>
<div class="list-group">
	<router-link :to="{name : 'BoardView', query : {boardNo : boardItem.no}}" class="list-group-item list-group-item-action" v-for="boardItem in boardList" v-bind:key="boardItem.no">
		<div class="list-group-item-article">
			<div class="list-group-item-writer mb-3">
				<span>{{boardItem.writerNickname}}</span>
				<small>{{elapsedText(boardItem.writedate)}}</small>
			</div>
			<h5 class="list-group-item-title mb-3">{{boardItem.subject}}</h5>
			<p class="list-group-item-content">{{boardItem.content}}</p>
		</div>
		<div class="list-group-item-thumbnail">
			<img :src="(boardItem.poster.toUpperCase().startsWith('HTTP') ? '' : 'http://localhost:9000') + boardItem.poster" alt="">
		</div>
	</router-link>
</div>

그러면 작성 날짜가 변경된 것을 확인할 수 있습니다.

 

 

SNS처럼 작성 날짜가 표시되는지 로그인하여 새 게시물을 작성해 보겠습니다.

 

1. 오른쪽 상단에 있는 로그인을 클릭합니다. 아이디는 "testid1", 패스워드는 "testpwd1"를 입력합니다.

 

2. 상단 배너에서 "나의 이야기 쓰기" 또는 "나의 반려생활 쓰기"를 클릭합니다.

 

3. 테스트를 위해 제목은 "분위기 좋은 남산 맛집", 내용은 "안녕하세요. 햇살이 좋은 날 연인과 함께 걷는다면 어떤 장소가 떠올리시나요? 저는 인천에 살고있어서 그런지, 햇살이 좋은 날이면 '남산'이 떠올리곤 합니다. 오늘 제가 소개해드릴 곳은 특별한 날 가지 좋은 남산에 근처에 있는 카페입니다."으로 입력하고 "등록"틀 클릭합니다.

 

4. 상단 메뉴에서 "Home"을 클릭합니다. 그럼 방금 등록한 게시물이 나타나고 작성 날짜가 "방금 전"으로 표시됩니다.

 

 

웹 브라우저를 새로 고침 할 때마다 작성 날짜가 지난 시간으로 표시되는 것을 확인할 수 있습니다.

 

아직 게시물 등록 페이지는 수정되지 않아서 대표 이미지 업로드 기능이 없습니다. (향후 추가 개발할 예정입니다.)

그래서 대표 이미지가 없을 경우에는 게시물의 이미지가 나타나지 않도록 수정하겠습니다.

 

웹 브라우저에서 F12 키를 누르고 개발자 툴로 이미지 URL를 확인하면 "http://localhost:9000/asset/board/files/undefined"인 것을 확인할 수 있습니다.

 

대표 이미지가 없을 경우에는 boardItem.poster가 "/asset/board/files/undefined"으로 전달되기 때문에 boardItem.poster가 "undefined"으로 끝나면 .list-group-item-thumbnail의 태그가 표시되지 않게 처리하면 됩니다.

<div class="list-group">
	<router-link :to="{name : 'BoardView', query : {boardNo : boardItem.no}}" class="list-group-item list-group-item-action" v-for="boardItem in boardList" v-bind:key="boardItem.no">
		<div class="list-group-item-article">
			<div class="list-group-item-writer mb-3">
				<span>{{boardItem.writerNickname}}</span>
				<small>{{elapsedText(boardItem.writedate)}}</small>
			</div>
			<h5 class="list-group-item-title mb-3">{{boardItem.subject}}</h5>
			<p class="list-group-item-content">{{boardItem.content}}</p>
		</div>
		<div class="list-group-item-thumbnail" v-if="!boardItem.poster.toUpperCase().endsWith('UNDEFINED')">
			<img :src="(boardItem.poster.toUpperCase().startsWith('HTTP') ? '' : 'http://localhost:9000') + boardItem.poster" alt="">
		</div>
	</router-link>
</div>

 

 

추가로 SNS처럼 사용자 프로필이 표시되게 수정하겠습니다.

 

1. 작성자 닉네임 앞에 프로필을 추가합니다. 작성자 프로필 이미지는 boardItem.writerProfile입니다.

<div class="list-group">
	<router-link :to="{name : 'BoardView', query : {boardNo : boardItem.no}}" class="list-group-item list-group-item-action" v-for="boardItem in boardList" v-bind:key="boardItem.no">
		<div class="list-group-item-article">
			<div class="list-group-item-writer mb-3">
				<div class="list-group-item-writer-profile">
					<img :src="(boardItem.writerProfile.toUpperCase().startsWith('HTTP') ? '' : 'http://localhost:9000') + boardItem.writerProfile" alt="">
				</div>
				<span>{{boardItem.writerNickname}}</span>
				<small>{{elapsedText(boardItem.writedate)}}</small>
			</div>
			<h5 class="list-group-item-title mb-3">{{boardItem.subject}}</h5>
			<p class="list-group-item-content">{{boardItem.content}}</p>
		</div>
		<div class="list-group-item-thumbnail" v-if="!boardItem.poster.toUpperCase().endsWith('UNDEFINED')">
			<img :src="(boardItem.poster.toUpperCase().startsWith('HTTP') ? '' : 'http://localhost:9000') + boardItem.poster" alt="">
		</div>
	</router-link>
</div>

 

2. <style>에 .list-group-item-writer-profile과 .list-group-item-writer-profile > img를 추가합니다.

.list-group-item-writer-profile {
  position: relative;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  overflow: hidden;
  margin-right: 10px;
  float: left;
}

.list-group-item-writer-profile > img {
  width: 100%;
  height: 100%;
}

 

SNS처럼 작성자 프로필 이미지가 표시되는 것을 확인할 수 있습니다.

728x90
반응형