Skip to content

LabofDev/EndofVuejs

Repository files navigation

1. Study Agenda

  • Github Branch Name : vue-endofvue-1.init
  • summary
    • 강좌에서 개발할 웹어플리케이션에 대해서 확인하고 일반적인 웹개발 절차와 API 문서에 대해서 알아본다.

1.1 Introduce Application

  • 회원가입, 로그인, CRUD가 발생하는 기본적인 웹어플리케이션

1.2 Procedure of Front-End Development

  • 웹 개발 절차
    • 요구사항 > 서비스 기획 > UI, UX 상세 설계 > GUI 디자인 > 퍼블리싱 > Back-End API 개발 > Front-End 개발 > QA
  • Front-End 개발자의 역할
    • Client UI에 대한 코드 작성
    • 기획, 디자인, 퍼블리싱, Back-End 개발자와 소통

1.3 Document of API

  • Swagger UI
    • Swagger는 개발자가 RESTfull API를 설계, 빌드, 문서화, 소비하는 일을 도와주는 오픈소스 소프트웨어 프레임워크

2. Configuration of Development Environment

  • Github Branch Name : vue-endofvue-2.environment
  • summary
    • 개발환경 구성에 대해서 알아본다(깃허브, 필수 설치 프로그램, )

2.1 개발 환경(Tools)

2.2 개발 환경 (Visual Studio Code Gitbash Terminal)

  • git bash는 Windows에서도 Linux 명령어를 사용할 수 있는 환경
  • Visual Studio Code에서도 gitbash 터미널을 사용할 수 있다.(본 강좌에서는 사용해야 함)
  • 기존의 terminal.integrated.shell.windows에 대해서 VSCode 업데이트 됨에 사용할 수 없음(참고)
  • Visual Studio Code > ctrl + , > terminal.integrated.shell.windows > setting.json open > 하기의 코드 추가
"terminal.integrated.profiles.windows": {
    "GitBash": {
        // 일반적으로 : "C:\\Git\\bin\\bash.exe"
        "path":["사용자별 git 설치 경로\\bash.exe"],
        "icon":"terminal-bash"
    },

    "PowerShell": {
        "source": "PowerShell",
        "icon": "terminal-powershell"
    },
    "Command Prompt": {
        "path": [
            "${env:windir}\\Sysnative\\cmd.exe",
            "${env:windir}\\System32\\cmd.exe"
        ],
        "args": [],
        "icon": "terminal-cmd"
    },
},
// "GitBash"로 설정하면 "Bash"가 기본으로 설정 됨
"terminal.integrated.defaultProfile.windows": "Command Prompt",

2.3 개발 환경(Node js)

  • Node 버전 확인 및 업그레이드 & 다운그레이드
    • 본 강좌의 소스는 node 버전이 v10.16.3으로 진행해야 함
    • 특정 버전의 Node를 설치하는 방법은 2가지
      • 원하는 LTS 버전을 찾아 해당 OS의 실행 파일로 설치 (GUI로 사용)
      • NVM을 사용하여 특정 버전 설치 사용등 (Command Line으로 사용)
    $ node -v
    v14.8.1
    • Node js - v10.16.3에서 OS 버전에 맞는 파일을 다운로드 받아 설치 가능
  • NVM(Node Version Manager)
    • NVM을 이용하여 nodejs 특정 버전을 설치, 업그레이드 및 다운그레이드 가능
    • Node js
    • NVM Github
    • NVM 설치
      $ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
    • NVM 명령어 등록
      • VSCode > 터미널을 Bash로 열고 하기 명령어 입력
      $ vi ~/.bashrc
      
      // i : 쓰기모드 진입
      export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
      [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
      // esc : 쓰기모드 해지
      // :wq : 저장하고 종료 (:q : 저장하지 않고 종료)
      • bash 터미널에서 하기 명령어를 입력하여 정상적으로 NVM이 설치 및 명령어가 적용되었는지 확인
      $ nvm --version
      0.39.0
      $ nvm install 10.16.3
      
      Downloading and installing node v10.16.3...
      Downloading https://nodejs.org/dist/v10.16.3/node-v10.16.3-win-x64.zip...
      ################################# 100.0% 
      Computing checksum with sha256sum
      Checksums matched!
      Now using node v10.16.3 (npm v6.9.0)
      Creating default alias: default -> 10.16.3 (-> v10.16.3)
      
      $ node -v
      v10.16.3
      • vue-til-server > package.json 파일내 패키지 설치를 위한 명령어 실행
      $ npm i
      • npm run dev로 서버 실행
      $ npm run dev
      
      ...중략...
      
      VUE TIL SERVER IS RUNNING ON 3000
      // '3000'에 대한 포트를 사용중이면 하기에서 변경 가능
      // src > app.js > port 번호 수정
      • http://localhost:3000/api/docs로 접속하여 API 서버 확인

2.4 개발 환경(MongoDB)

  • MongoDB Site
  • src > app.jsconst db에서 하기의 생성된 DB를 연결
  • bash command > npm run dev > Server 기동 > http://localhost:3000/api/docs 접속 > /signup API Test

2.5 개발 환경(Swagger UI API)

  • Node js로 작서된 Back-End API 코드를 Front-End 개발자와 커뮤니케이션하기 위한 API UI
  • RESTfull API를 직접 테스트 할 수 있는 API UI

2.6 개발 환경(프로젝트 구성 - 프로젝트 생성, ESLint & Prettier)

  • 웹팩 데브 서버 설명 글
  • Vue.js 개발 생산성을 높여주는 도구 3가지
  • vue --version 으로 vue/cli 버전 확인
  • vue create vue-til으로 프로젝트 생성 (Manual로 선택하며, 하기의 이미지 참고)
  • ESLint 구문 오류(ESLint 란?)
    • 해결 방법
      • vue.config.js 파일 생성하고 하기와 같은 코드 삽입
      • 빌드 오류는 발생하나 오버레이 안됨
      module.exports = {
        devServer: {
          overlay: false,
        },
      };
    • ESLint 설정 파일 설정 방법
      • root > .eslintrc.js 파일에 하기와 같이 개발자 커스텀 설정 가능
    rules: {
      "no-console": "error", // 정상을 위하여 "off"로 설정 필요
      // "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
      // "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
    },
    • console 명령서 사용시 error 출력으로 설정함으로 하기와 같이 오류 발생
    • ESLint 구문 오류시 해당 구문에 빨간줄이 생기지 않을 경우
      • VSCode 설정 > settings.json에 하기 코드 추가
      "editor.codeActionsOnSave": {"source.fixAll.eslint": true},
      "eslint.workingDirectories": [{"mode": "auto"}],
  • Prettier (일관성 있는 코드 포맷을 위한 웹팩)
    • 하기의 이미지처럼 prettier 구문을 적용하면 코드에 붉은색 라인이 생기며 빌드 오류 발생
    • 해결 방법
      • 설정eslint: validate > settings.json 파일에서 하기 축
      "editor.codeActionsOnSave": {
            "source.fixAll.eslint": true
        },
        "eslint.workingDirectories": [
            {
                "mode": "auto"
            }
        ],
        // ESLint
        "eslint.validate":[
            "vue", 
            "javascript", 
            "javascriptreact", 
            "typescript", 
            "typescriptreact"
        ],
        // don't format on save
        "editor.formatOnSave": false,
      • prettier 확장 프로그램에서 사용안함(작업영역) 설정
      • 설정Format on Save 체크 해지

2.7 개발 환경(기타)

3. Router & Design of Component

// vue-til 소스를 최초에 설치할 경우
$ git clone https://github.com/joshua1988/vue-til
$ git checkout 1_setup
$ npm i

3.1 Router

  • LoginPage, SignupPage 생성 및 라우터 설정
  • router 설치
$ npm i vue-router
  • Code Splitting(코드 스플리팅)
    • 최초 로딩시 리소스를 최소화하여 가져오고 이후 뷰페이지 호출시 리소스를 로딩함
    export default new VueRouter({
      routes: [
        {
          path: '/login',
          component: () => import('@/views/LoginPage.vue'),
        },
        {
          path: '/signup',
          component: () => import('@/views/SignupPage.vue'),
        },
      ],
    });
  • 뷰 라우터 오픈 소스
  • 뷰 라우터 History Mode 주의 사항 문서
    • history 모드의 경우 URL에 #이 없이 나타난다.(서버가 새로운 페이지로 인식하지 못 함)
    • 서버가 새로운 페이지로 인식을 못 함으로 서버에 설정이 필요함.
  • fallback router
    • 지정된 경로가 아닌 경우를 대비
export default new VueRouter({
	mode: 'history',
	routes: [
		{
			path: '/login',
			component: () => import('@/views/LoginPage.vue'),
		},
		{
			path: '/signup',
			component: () => import('@/views/SignupPage.vue'),
		},
		{
			path: '*',
			component: () => import('@/views/NotFoundPage.vue'),
		},
	],
});

4. Development of Member Register

5. Configuration of Project

6. Development of Login

7. Store & Login State Management

  • Github Branch Name : vue-endofvue-7.store_state
  • summary
    • Style, Main Page

7.1 Style

  • css/common.css, css/reset.css
  • App.vue, AppHeader.vue
  • vue router > router-link를 사용(html 레벨)
  • vue router > router.push를 사용(javascript 레벨)

7.3 로그인 후 사용자 정보 표시(store를 사용)

8. API Authentication by Token

9. Development of Retrieve Note Data

  • Github Branch Name : vue-endofvue-9.dev_retrieve
  • summary
    • 목록 조회 개발, style 적용, 컴포넌트화, 로딩 상태 & 로딩 스피너 개발
  • PostItem에 대하여 컴포넌트화 (MainPage에서 props로 컴포넌트로 값 전달)
// MainPage.vue
<template>
	<div>
		<div class="main list-container contents">
			<h1 class="page-header">Today I Learned</h1>
			<ul>
				<PostListItem
					v-for="postItem in postItems"
					v-bind:key="postItem._id"
					v-bind:postItem="postItem"
				></PostListItem>
			</ul>
		</div>
	</div>
</template>
export default {
	components: {
		PostListItem,
	},
}

// PostListItem.vue
<template>
	<li>
		<div class="post-title">
			{{ postItem.title }}
		</div>
		<div class="post-contents">
			{{ postItem.contents }}
		</div>
		<div class="post-time">
			{{ postItem.createdAt }}
		</div>
	</li>
</template>

<script>
export default {
	props: {
		postItem: {
			type: Object,
			required: true,
		},
	},
};
</script>

10. Management of Authentication Value using Browser Storage

  • Github Branch Name : vue-endofvue-10.mgmt_auth_browser_storage
  • summary
    • 로그인한 세션 정보를 위하여 쿠키 사용

10.1 Cookie Save

  • 쿠키 저장
  • store > index.jsstate에서 쿠키값 보존을 위한 코드 추가
state: {
		username: getUserFromCookie() || '',
		token: getAuthFromCookie() || '',
	},

10.2 코드 정리

  • async, await를 왜 사용하는지 반드시 인지하고 넘어 가야 함
  • 자바스크립트는 기본이 비동기 처리 방식이나 async, await를 사용하여 동기화 처리 필요

11. Development of Create Note Data

12. Middle Adjustment

12.1. 개발환경 설정

  • 개발 툴 및 필요 프로그램 설치
  • API 서버 프로젝트 클론

12.2. Vue CLI 프로젝트 생성

  • Prettier
  • ESLint
  • jsconfig

12.3. 뷰 라우터 및 컴포넌트 설계

  • <router-link>
  • <router-view>
  • mode: history와 서버 배포시 주의 사항
  • 코드 스플리팅 component: () => import('경로')

12.4. 회원 가입 페이지 개발

  • 사용자 폼 처리
  • async & await
  • axios
  • swagger API 문서 보는 방법

12.5. 실무 환경 구성

  • axios.create()
  • env 파일 설정 방법
  • Vue CLI 버전 3 이상에서의 env 파일 규칙

12.6. 로그인 페이지 개발

  • 사용자 폼 처리 기능 구현
  • async & await 에러 처리 방법
  • 사용자 폼 유효성 검사

12.7. 로그인 상태 관리

  • 뷰엑스를 이용한 사용자 아이디 관리
  • this.$router.push('/main')

12.8. API 인증 처리를 위한 토큰 관리

  • JSON Web Token
  • Authorization 토큰 값으로 API 인증을 받는 방법
  • axios.interceptors

12.9. 학습 노트 데이터 조회

  • 학습 노트 목록 표시 기능 구현
  • 목록 아이템 컴포넌트화
  • 스피너를 이용한 데이터 로딩 상태 표시

12.10. 브라우저 저장소를 이용한 인증 값 관리

  • 쿠키를 이용한 로그인 인증 값 저장
  • actions를 이용한 컴포넌트 로직 정리

12.11. 학습 노트 데이터 생성

  • 학습 노트 생성 기능 구현
  • 학습 노트 본문 길이 유효성 검사

12.12 13부터 진행할 내용

  • 데이터 성격 별로 API 함수 모듈화
  • 학습 노트 삭제 기능
  • 날짜 형식 포맷팅 filter
  • 라우터 심화
  • 프런트엔트 테스트

13. API Function Modularity

  • Github Branch Name : vue-endofvue-13.api_modularity
  • summary
    • API function의 모듈화로 기능의 성격별로 또는 서브-도메인의 성격별로 분리(모듈화) 진행
    • 추후 시스템이 확장될 경우를 대비하여 처음부터 확장성을 고려하여 API 호출 함수 모듈화 필요
  • Key Sample Code
    • api > index.js 파일에서 기능적으로 유사한 함수끼리 모아 분리 모듈화
    • 인스터스를 2개로 구분하는데 인증전(로그인전)인증후(로그인후)
    import axios from 'axios';
    import { setInterceptors } from './common/interceptors';
    
    // 엑시오스 초기화 함수 (인증전 즉, 로그인 전)
    function createInstance() {
      return axios.create({
        baseURL: process.env.VUE_APP_API_URL,
      });
    }
    
    // 액시오스 초기화 함수 (인증후 즉, 로그인 후)
    function createInstanceWithAuth(url) {
      const instance = axios.create({
        baseURL: `${process.env.VUE_APP_API_URL}${url}`,
      });
      return setInterceptors(instance);
    }
    export const instance = createInstance();
    export const posts = createInstanceWithAuth('posts');
    • api > auth.js 로그인 관련 API function 모듈
    // 로그인, 회원 가입, 회원 탈퇴 등을 위한 API
    import { instance } from './index';
    
    // 회원가입 API
    function registerUser(userData) {
      return instance.post('signup', userData);
    }
    
    // 로그인 API
    function loginUser(userData) {
      return instance.post('login', userData);
    }
    
    export { registerUser, loginUser };
    • api > posts.js 게시물 관련 API function 모듈
    // 학습 노트 조작과 관련된 CRUD API 함수
    import { posts } from './index';
    
    // 학습 노트 데이터를 조회하는 API
    function fetchPosts() {
      return posts.get('/');
    }
    
    // 학습 노트 데이터를 생성하는 API
    function createPost(postData) {
      return posts.post('/', postData);
    }
    
    export { fetchPosts, createPost };

14. & 15. Development of Delete Note Data & Modify Note Data

  • Github Branch Name : vue-endofvue-14.dev_delete
  • summary
  • 게시물 삭제
    • API에 삭제 함수 생성 PostListItem에서 deleteItem 이벤트 처리
    async deleteItem() {
      		if (confirm('You want to delete it?')) {
      			await deletePost(this.postItem._id);
      			this.$emit('refresh');
      		}
      	},
  • 게시물 수정
    • API에서 id를 인자로 게시물을 조회하는 함수 추가
    • PostEditPage.vuePostEditForm.vue를 생성하여 데이터 변경
  • 삭제수정push를 사용하여 main으로 이동
this.$router.push('/main');

16. Data Formatting

  • Github Branch Name : vue-endofvue-16.data_format
  • summary
  • 16.1 Filter 함수 사용
    • utils > filters.js 정의
    export function formatDate(value) {
      const date = new Date(value);
      const year = date.getFullYear();
      let month = date.getMonth() + 1;
      month = month > 9 ? month : `0${month}`;
      const day = date.getDate();
      let hours = date.getHours();
      hours = hours > 9 ? hours : `0${hours}`;
      const minutes = date.getMinutes();
      return `${year}-${month}-${day} ${hours}:${minutes}`;
    }
    • main.js에 전역으로 사용할 필터 정의
    //main.js
    import { formatDate } from '@/utils/filters';
    Vue.filter('formatDate', formatDate);
    • 전역으로 정의한 필터 사용
    <div class="post-time">
      {{ postItem.createdAt | formatDate }}
      <i class="icon ion-md-create" @click="routeEditPage"></i>
      <i class="icon ion-md-trash" @click="deleteItem"></i>
    </div>

17. Router Advanced

//routes > index.js
const router = new VueRouter({
	mode: 'history',
	routes: [
    {
      path: '/post/:id',
      component: () => import('@/views/PostEditPage.vue'),
      meta: { needAuth: true },
    },
  ]
});

router.beforeEach((to, from, next) => {
	if (to.meta.needAuth && !store.getters.isLogin) {
		console.log('인증이 필요합니다.');
		next('/login');
		return;
	}
	next();
});

18. Introduce of Front-End Testing

  • Github Branch Name : vue-endofvue-18.front_end_test
  • summary
  • 18.1 Jest 테스트를 위한 환경 설정
    //`package.json` 설정
      "scripts": {
      "test": "vue-cli-service test:unit --watchAll",
    },
    //--watchAll : 변경즉시 반영 됨
    //jest.config.js 설정
    module.exports = {
      preset: '@vue/cli-plugin-unit-jest',
      testMatch: [
        '<rootDir>/src/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)',
      ],
    };
    // 테스트 실행 명령어
    $npm t
    // jest 함수들의 eslint 인식 빨간펜 처리
    // .eslintrc.js
    env: {
      node: true,
      jest: true,
    },
    
  • 18.2 Jest 일반 사항
    • 제일 먼저 jest.config 파일의 설정을 참고하며, 설정 파일이 없다면 하기의 기본 설정 폴더를 참고
      • test > unit > *.spec.js
    • 일반적으로 테스트 스크립트 파일은 테스트 대상의 인근에 생성하고 관리
    • 또는 jest.config.js파일에서와 같이 __tests__와 같이 폴더 생성하여 관리
  • 18.3 Jest Test Code 작성
    • vuejs test library
    import { shallowMount } from '@vue/test-utils';
    • find() function sample
    import LoginForm from './LoginForm.vue';
    import { shallowMount } from '@vue/test-utils';
    
    describe('LoginForm.vue', () => {
      test('ID는 이메일 형식이여야 한다.', () => {
        const wrapper = shallowMount(LoginForm);
        const idInput = wrapper.find('#username');
        console.log(idInput.html());
      });
    });
    • 인스턴스를 생성한 vue 페이지의 computed에도 접근 가능
    describe('LoginForm.vue', () => {
      test('ID는 이메일 형식이여야 한다.', () => {
        const wrapper = shallowMount(LoginForm, {
          data() {
            return {
              username: '[email protected]',
            };
          },
        });
        const idInput = wrapper.find('#username');
        console.log('InputBox Value ', idInput.element.value);
        console.log(wrapper.vm.isUsernameValid);
      });
    });
    • LoginForm.vue에서 로그인 버튼의 활성/비활성화 코드 추가후 테스트 코드 작성
    // LoginForm.vue
    <button
      :disabled="!isUsernameValid || !password"
      type="submit"
      class="btn"
      v-bind:class="!isUsernameValid || !password ? 'disabled' : null"
    >
    
    // LoginForm.spec.js
    test('#3. ID와 PWD가 입력되지 않으면 로그인 버튼이 비활성화 된다.', () => {
      	const wrapper = shallowMount(LoginForm, {
      		data() {
      			return {
            // 값이 없으면 통과, 값이 있으면 실패
      				username: '[email protected]',
      				password: '1234',
      			};
      		},
      	});
      	const button = wrapper.find('button'); // '.btn'
      	expect(button.element.disabled).toBeTruthy();
      });

19. End of

  • Github Branch Name : vue-endofvue-19.end
  • summary
    • 프로젝트 구성 방법
      • ESLint, Prettier
      • env
    • REST API를 이용한 CRUD Application 구현 방법
    • Back-End API 문서 보는 방법 및 Back-End 개발자와 협업할때의 주의 사항
    • axios 인터셉터와 모듈화를 이용한 API 함수 설계
    • Router 페이지 권한 처리
    • Front-End 테스트 방법