반응형

 

npm install -S yup

 

기본사용법

import * as yup from 'yup';

let schema = yup.object().shape({
  name: yup.string().required(),
  age: yup.number().required().positive().integer(),
  email: yup.string().email(),
  website: yup.string().url(),
  createdOn: yup.date().default(function () {
    return new Date();
  }),
});

// check validity
schema
  .isValid({
    name: 'jimmy',
    age: 24,
  })
  .then(function (valid) {
    valid; // => true
  });

// you can try and type cast objects to the defined schema
schema.cast({
  name: 'jimmy',
  age: '24',
  createdOn: '2014-09-23T19:25:25Z',
});

사용방식 1

<Form :validation-schema="schema">
    <input
      v-model="data.name"
    />
    <input
      type="number"
      v-model="data.number"
    />
    <input
      v-model="data.email"
    />
  </div>
  ~~~~
  <button
      @click="onSubmit(data)"
      >저장</button
    >
</Form

~~

data() {
    const schema = Yup.object().shape({
      name: Yup.string().required("require name"),
      number: Yup.number().required("require num"),
      email: Yup.string()
        .email("is not email")
        .required("require email"),
    });
    return {
      schema,
      data: {
        name: "",
        number: "",
        email: "",
      },
    };
  },
  
methods: {
  onSubmit(values) {
    this.schema.isValid(values).then((valid) => {
      // valid true or false
      if (valid) {
        alert("통과");
      } else {
        alert("오류");
      }
    });
  },

Form 필드로 submit 을 하지 않고, 비동기식으로 전송하는 방식이고, 이 방식이 더 나은듯 하다. 이유는 아래 기입

사용방식 2

<Form @submit="onSubmit" :validation-schema="schema">
    <input
      name="data.name"
    />
    <input
      type="number"
      name="number"
    />
    <input
      v-model="data.email"
    />
  </div>
  ~~~~
  <input
      type="submit"
      @click="onSubmit"
      >저장</input
    >
</Form

~~

data() {
    const schema = Yup.object().shape({
      name: Yup.string().required("require name"),
      number: Yup.number().required("require num"),
      email: Yup.string()
        .email("is not email")
        .required("require email"),
    });
    return {
    	schema
    };
  },
  
methods: {
  onSubmit(values) {
    this.schema.isValid(values).then((valid) => {
      // valid true or false
      if (valid) {
      } else {
      }
    });
  },

Form element 에서 submit 을 할 경우가 일반적이지만, validation 이 false 가 한개라도 있는 경우 버튼을 누르더라도 onSubmit 이벤트가 발생하지 않는다.

 

 

 

 

https://github.com/jquense/yup [공식문서]

반응형
반응형

vue 에서 vuex 를 사용하여 store 를 만들면,

getters 의 경우 this.$store.getters.[getter이름] 로 사용가능

actions 의 경우 this.$store.dispatch(‘[action이름]’, data) 과 같이 사용할 수 있다.

또는 직접 state 에 바로 접근해서 this.$store.state.book.message 로도 사용가능은 하다.

 

helper 없이 기본적으로 사용

<template>
  <div id="app">
    <div>
      <label>{{getMsg}}</label>

      <br/>

      <button @click="onChangedMsg">Click</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'app',
  computed: {
    getMsg () {
      return this.$store.getters.getMsg
    }
  },
  methods: {
    onChangedMsg () {
      this.$store.dispatch('callMutation', { newMsg: 'World !!' })
    }
  }
}
</script>

 

store/index.js

store 를 하나만으로는 사용하기 어려우므로 기능별 또는 페이지별로 분리해야한다.

기능/페이지별로 store를 분리하고, 하나의 store에는 state, mutations, actions, getters를 포함해서 관리하는게 편하다.

index 파일에서 여려개의 모듈을 import 하는 역할이다.

import Vue from 'vue'
import Vuex from 'vuex'

import BookStore from './module/book'

Vue.use(Vuex)

export default new Vuex.Store({
    modules: {
        book: BookStore
    }
})

store/module/book.js

// state
const state = {
    message: 'Hello'
}

// mutations
const mutations = {
     changeMessage (state, newMsg) {
      state.message = newMsg
    }
}

// actions
const actions = {
    callMutation ({ state, commit }, { newMsg }) {
      commit('changeMessage', newMsg)
    }
}

// getters
const getters = {
    getMsg (state) {
      return `${state.message} => Length : ${state.message.length}`
    }
}

export default {
  state,
  mutations,
  actions,
  getters
}

위와 같이 한 페이지별 기능별로 js 파일을 따로 모듈로 관리하여 둔다면 관리하기 편하다.

 

Vuex Binding Helper

위에 설명했던 예시처럼 사용할 수도 있지만 vuex 에는 binging helper 라는 util 이 존재하여 좀 더 간편하게 사용이 가능하다.

 

Helper는 state, mutations, actions, getters 별로 각각 mapState, mapActions, mapMutations, mapGetters가 존재하고 아래처럼 바인딩할 수 있다.

 

import { mapState, mapActions, mapMutations, mapGetters } from 'vuex'

export default {
  name: 'Sample',
  computed: {
    ...mapState('book', {
        message: state => state.message     // -> this.message
    }),
    ...mapGetters('book', [
       'getMsg'       // -> this.getMsg
    ])
  },
  methods: {
    ...mapMutations('book', [
        'mutation이름'     // -> this.mutation이름() 으로 mutation 접근
    ]),
    ...mapActions('book', [
        'action이름'      // -> this.action이름() 으로 action call가능
    ])
  }
}

 

Vuex createNamespacedHelpers

mapHelper를 통해 개발이 가능하지만 좀 더 편리하고 효율적으로 하기 위해서는 createNamespacedHelpers를 사용하는 것이 좋다. 어떻게 보면 결과적으로나 사용되는 Util은 동일하다.

 

import { createNamespacedHelpers } from 'vuex'

const 
    bookHelper = createNamespacedHelpers('book'),
    bookListHelper = createNamespacedHelpers('bookList')

export default {
  name: 'Sample',
  computed: {
    ...bookHelper.mapState({
        message: state => state.message     // -> this.message
    }),
    ...bookHelper.mapGetters([
       'getMsg'       // -> this.getMsg
    ]),
    ...bookListHelper.mapState({
        messageList: state => state.messageList     // -> this.messageList
    }),
    ...bookListHelper.mapGetters([
       'getMsgList'       // -> this.getMsgList
    ])
  },
  methods: {
    ...bookHelper.mapMutations([
        'changeMessage'     // -> this.changeMessage()
    ]),
    ...bookHelper.mapActions([
        'callMutation'      // -> this.callMutation()
    ]),
    ...bookListHelper.mapMutations([
        'changeMessageList'     // -> this.changeMessageList()
    ]),
    ...bookListHelper.mapActions([
        'callMutationList'      // -> this.callMutationList()
    ]),
  }
}

 

반응형
반응형

vue3 를 사용하게 되면 vuex4 를 사용하여 store 를 관리 할 수 있다.

 

vuex 4 설치

npm install --save vuex@4.0.1   

 

store 작성

 vuex 의 store를 module 형식으로 구성

|-- store

|    |-- index.js

|    |-- mutation-types.js

|    |-- modules

|         |-- person.js

   

  • mutation-types.js : 뮤테이션 타입 정의를 담당 
  • modules : vuex 에서 각 모듈들을 담아두는 폴더 
  • index.js : vuex 의 store 를 정의

다음과 같은 구조로 해야 소스관리가 편하다.

mutation-types.js 작성

export const PERSON = {
    SET_NAME: 'SET_NAME', // 이름을 변경하는 타입 정의
};

person.js 모듈 작성

방법1

import { PERSON } from '../mutation-types';

const state = {
    name: '',
    age: 0,
}

const getters = {
    personInfo: (state) => {
        return `이름 : ${state.name}, 나이 : ${state.age}`;
    }
}

const actions = {
    changeName({ commit }, value){
        commit(PERSON.SET_NAME, value);
    },
}

const mutations = {
    [PERSON.SET_NAME](state, value) {
        state.name = value;
    }
}

export default {
    namespaced: true,
    state,
    getters,
    actions,
    mutations
}

방법2

import { PERSON } from '../mutation-types';


export const person = {
  state: () => ({
    name: '',
    age: 0,
  }),
  mutations: {
    [PERSON.SET_NAME](state, value) {
      state.name = value;
    }
  },
  getters: {
    personInfo(state) {
      return `이름 : ${state.name}, 나이 : ${state.age}`;
    }
  },
  actions: {
    changeName({ commit }, value){
        commit(PERSON.SET_NAME, value);
    },
  }
};

store 의 모듈들은 state, mutations, getters, actions 라는 속성으로 이루어져야 store 를 관리 하기 수월하다.

 

참고 : namespace 

store 를 모듈화 할떄는 어떤 스토어에서 어떤 모듈을 사용했는지 헷갈리므려 namespaced 를 꼭 사용해서

person/personInfo 등등으로 이름으로 호출 가능

 

namespace 를 사용시 

state는 기존대로 state.moduleName.stateName으로 호출

getters computed(() => store.getters["moduleName/getterName"])으로 호출

mutation store.commit("moduleName/mutationName", params)으로 호출

action store.dispatch("moduleName/actionName", params)으로 호출

 

index.js (스토어 선언 작성)

스토어를 선언하는 방법은 이전과 달리 createStore 를 통해 생성한다

import { createStore } from 'vuex';
import person from './modules/person';


export default createStore({
    modules: {person},
})

여러개 모듈로 할 경우 아래와 같이 mudules 에 추가하면 된다.

import { createStore } from "vuex";
import { Counter } from "@/store/modules/Counter";
import { moduleA } from "@/store/modules/moduleA";

export default createStore({
  modules: { Counter, moduleA }
});

 

store 연결하기 (main.js 작성)

 위의 작성된 코드를 main.js 에서 연결을 해야 한다.

스토어의 연결을 위해 위에서 작성한 스토어 선언 파일을 불러와서 .use() 를통해 사용하도록 해주면 vuex 설정이 완료된다.

import { createApp } from "vue";
import App from "./App.vue";
import store from "./store";

createApp(App)
  .use(store)
  .mount("#app");

 

useStore

 vuex 4 에서 새로나온 useStore 는 스토어를 사용하는 새로운 방법

const store = useStore(); // 스토어 호출
 
const name = computed(() => store.state.person.name); // state 정보 가져오기
const personInfo = computed(() => store.getters['person/personInfo']); // getters 가져오기
const changeName = e => store.dispatch('person/changeName', e.target.value); // 액션함수 

 

useStore를 사용한 예제 코드 작성

<template>
    <div id="app">
        <h2>{{name}}</h2>
        <p>{{personInfo}}</p>
        <input type="text" :value="name" @input="changeName" placeholder="이름을 작성해 주세요."/>
    </div>
</template>

<script>
    import {computed} from 'vue';
    import {useStore} from 'vuex';

    function usePerson() {
        const store = useStore();

        const name = computed(() => store.state.person.name);
        const personInfo = computed(() => store.getters['person/personInfo']);
        const changeName = e => store.dispatch('person/changeName', e.target.value);

        return {
            name,
            personInfo,
            changeName
        }
    }

    export default {
        name: 'App',
        setup() {
            return {
                ...usePerson()
            }
        },
    }
</script>

 

참고문헌

> https://kyounghwan01.github.io/blog/Vue/vue3/composition-api-vuex/#vuex-%E1%84%89%E1%85%A6%E1%84%90%E1%85%B5%E1%86%BC-%E1%84%86%E1%85%B5%E1%86%BE-store-module-1%E1%84%80%E1%85%A2%E1%84%85%E1%85%A9-%E1%84%89%E1%85%B5%E1%86%AF%E1%84%92%E1%85%A2%E1%86%BC [vue3에서 vuex 사용법 한글설명 자세함]

> https://next.vuex.vuejs.org/guide/migrating-to-4-0-from-3-x.html [공식문서]

반응형
반응형

실행 모드

vue cli 에는 기본적으로 3개 모드가 있다. vue cli 뿐 아니라 다른곳에서도 마찬가지.

1) development, 2) production, 3) test

그 외에 사용자가 정의한 모드를 추가 할 수 있다.

 

사용자 정의 모드

 // 최상단 경로 /package.json

"scripts": {
  .. 생략
   // local 로컬 모드 추가
  "local": "vue-cli-service serve --mode local",
  "mymode": "vue-cli-service serve --mode mymode", // 임의로 생성
   .. 생략
},
> npm run local
> npm run mymode

로 정의된 모드를 실행할 수 있다.

 

환경변수

실행 모드에 따라 변수 설정을 달리 할 수 있다.

기본적으로 2개의 환경 변수 NODE_ENV, BASE_URL 이 있다.

그 외에 환경변수 파일을 만들어 사용자가 정의한 변수를 추가 할 수 있다.

환경변수들은 process.env 객체에 정의되어 있고, 어디서나

console.log(process.env);

로 변수 값을 확인할 수 있다.

 

NODE_ENV

앱이 실행되는 모드이다.

3개의 기본 모드 "development", "production", "test" 가 있고, 사용자 정의 모드를 추가 할 수 있다.

모드 별로 기본적으로 사용하는 명령어가 있다.

  • development  ->  vue-cli-service serve
  • test -> vue-cli-service test:unit
  • production -> vue-cli-service build 와 vue-cli-service test:e2e

 

BASE_URL

vue.config.js의 publicPath 옵션에 해당하고 앱이 배포되는 기본 경로이다.

 

환경변수

환경변수 파일 생성방법

// 파일 경로: /.env.local, 파일은 반드시 루트 디렉토리에 생성해야 한다.

NODE_ENV = "local"

BASE_URL: "/"

VUE_APP_MYCONST = "아무변수 선언한거"

사용자 정의 변수 생성은 아래와 같이 생성한다.

VUE_APP_생성할변수명 = 값

 

환경변수 파일 룰

.env                # 모든 경우에 로드
.env.local          # 모든 경우에 로드 되지만 git 에 무시됨.
.env.[mode]         # 특정명의 모드에만 로드됨
.env.[mode].local   # 특정명의 모드에만 로드되지만 git 에 무시됨.

 

포트 변경

// /vue.config.js
module.exports = {
    devServer: {
        // 사용자 정의 환경 변수에서 VUE_APP_PORT가 있으면 사용하고
        // 없으면 3000 포트로 개발서버를 실행합니다.
        port: process.env.VUE_APP_PORT || 3000
    }
}

현재 실행하려는 모드가 local

npm run local 로 실행하고 .env.local 파일에 VUE_APP_PORT 변수가 있다면 해당 포트로 실행되고, 

정의된게 없다면 3000 포트로 실행하게 된다.

 

.env.local  

기본적으로 vue cli 로 프로젝트 생성시 .gitignore 파일에 .env.local 파일은 제외되어 있다.

해당 파일을 공유하려면 .gitignore 파일을 수정해야 한다.

// 파일 위치: /.gitignore

# local env files
.env.local
.env.*.local

 

참고문헌

https://cli.vuejs.org/guide/mode-and-env.html#modes

반응형
반응형

 

node-sass 버젼과 node 버젼이 맞지 않을떄 빌드시 서버에서 다음과 같은 오류 가 낫다.

 

npm ERR! code 1
npm ERR! path ~~~~node_modules/node-sass
npm ERR! command failed
npm ERR! command sh -c node scripts/build.js

 

로컬에서는 npm start 로 실행시

Syntax Error: Error: Node Sass does not yet support your current environment: OS X 64-bit with Unsupported runtime (88)
과 같은 오류가 난다.

 

오류 원인은 node 버젼이 해당 Node-sass 버젼을 지원하지 않기 때문에 발생한다

 

 

노드 버젼이 호환하는 node-sass 버젼 표이다.

NodeJSSupported node-sass versionNode Module

 

Below is a quick guide for minimum and maximum support supported version of node-sass

nodejs node-sass version node module
Node 16 6.0+ 93
Node 15 5.0+ 88
Node 14 4.14+ 83
Node 13 4.13+, <5.0 79
Node 12 4.12+ 72
Node 11 4.10+, <5.0 67
Node 10 4.9+, <6.0 64
Node 8 4.5.3+, <5.0 57
Node <8 <5.0 <57

출처 : www.npmjs.com/package/node-sass  

 

예를 들어 node 15 버젼인 경우 node sass 는 5 버젼 밑에는 호환이 안된다.

 

nodejs 를 버젼에 맞게 설치하든지 node-sass 를 새로 설치하는 방법이 있다.

 

node-sass 삭제 후 새로 설치

# 4.14.x 구 버전 설치되어 있음
$ npm list | grep node-sass 
├── node-sass@4.14.1 

# node-sass 삭제 후 새로 설치
$ npm uninstall node-sass 
$ npm install --save node-sass 

# 5.0.0 최신버전 설치 확인
$ npm list | grep node-sass 
├── node-sass@5.0.0

 

node-sass 는 node 버젼에 의존적이라 node 버젼 업데이트시 node-sass 버젼도 확인해야 한다.

반응형

'프론트엔드 > NPM' 카테고리의 다른 글

[Node] Mac OS에서 NVM 설치 및 사용 명령어 정리  (0) 2024.01.15
반응형

개발 환경 : mac

 

Flutter 를 시작하려면 문서부터 보고 차근차근 하면 된다.

flutter-ko.dev/docs/get-started/install/macos

 

일단 flutter sdk 다운부터 하자

flutter 다운

$ cd ~/development
$ unzip ~/Downloads/flutter_macos_2.0.3-stable.zip

다운받고 압축풀고, path 에 추가하자

 $ export PATH="$PATH:`pwd`/flutter/bin"

 위 명령어는 압축을 푼 경로에서 실행해야 한다.

또 위 명령어로 실행 하면 영구적이지 않고 터미널 열때마다 해당 작업을 해야 한다.

해서 위 명령어를 전역 path 로 저장하자.

 

영구적인 flutter 경로 등록

$ vi ~/.bash_profile

bash_profile 들어가서 

export PATH="$PATH:경로/flutter/bin"

export PATH="$PATH:[flutter sdk 압축 푼 경로]flutter/bin" 

다음과 같이 입력 후 파일 나와서

$ source ~/.bash_profile

로 적용해주고 

$ echo $PATH

또는

$ which flutter

로 확인해보자

 

여기까지만 하고 flutter 명령어로 어플리케이션 개발을 할 수 있다.

 

하지만 안드롱이드 스튜디오로 개발하는것이 용이하기 때문에 안드로이드 스튜디오를 다운받자

 

안드로이드 스튜디오로 flutter 를 개발하려면 flutter plugin 부터 다운받아야 한다.

 

 

configure 에서 plugins 메뉴에서 

flutter 플러그인을 설치후 ide를 재실행하자.

Application 선택 후

 

sdk 경로를 앞서 받았던 flutter 경로의 skd 경로로 설정해주면 된다.

 

여기까지 오면 모든 완료가 되었고, 원하는 기기 버젼 연결 후 실행 하면 끝이다.

 

참고

flutter-ko.dev/docs/get-started/install/macos [flutter 튜토리얼]

 

반응형
반응형

 MacOs catalina 

에서 플러터 깔고 안드로이드 스튜디오로 안드로이드 폰 usb 연결해서 빌드 시키면 

실행이 안된다.

아이폰 시뮬레이터는 실행되고 안드로이드만 동작하지 않는다.

 

에러문구는 아래와 같다.

 

FAILURE: Build failed with an exception.

 

* What went wrong:
Could not determine the dependencies of task ':app:compileDebugJavaWithJavac'.

> Failed to install the following Android SDK packages as some licences have not been accepted.

~~~~~

 

라이센스가 없다는 의미로 터미널로 입력하면 된다.

$ flutter doctor --android-licenses

여러 질문이 있는데 다 y yes 를 하면 된다.

 

명령어 치고 yes 를 입력한 뒤 바로 빌드하면 헬로우 월드 앱이 잘 실행된다.

 

참고

https://stackoverflow.com/questions/60467477/android-sdk-tools-option-is-missing-from-sdk-manager-in-android-studio-3-6-1
반응형
반응형

vue prop data 를 사용하다가

 

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders

이 오류를 본 적이 있다.

 

이 오류가 난 경우는 props data 를 받고 이 props 데이터를 변경해주었더니 생긴 오류다.

이 props 데이터를 위로 올리고 이 올린 데이터를 또 props 로 내려서 하위 자식에서 쓰려했더니 생긴 오류다.

 

props 에 대한 vue 의 공식 문서 설명이다.

일반적으로 prop을 변경시키고 싶은 유혹을 불러 일으킬 수있는 두 가지 경우가 있습니다.
1. 이 prop는 초기 값을 전달 하는데만 사용되며 하위 컴포넌트는 이후에 이를 로컬 데이터 속성으로 사용하기만 합니다.
2. prop는 변경되어야 할 원시 값으로 전달됩니다.

https://kr.vuejs.org/v2/guide/components.html#%EB%8B%A8%EB%B0%A9%ED%96%A5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%9D%90%EB%A6%84

- vue 공식문서

 

자식으로 내려온 props 데이터를 고치면 부모 데이터도 수시로 바뀐다고 생각했지만, 데이터 흐림이 단방향이므로 하위 데이터를 바꾼다 하더라도 부모의 데이터를 바꿀수 없었다..

 

그래서 eventBus 를 통해서 부모의 데이터를 바꿔준다.

 

여기까지는 괜찮지만 초기 렌더링시에 문제가 생길 수 있다.

이유는 데이터가 비동기적인 데이터를 가져오기 때문인데, 

 

인스턴스 생성 순서는 부모 -> 자식이며, 마운팅 순서는 자식->부모 

이기 떄문에 비동기적으로 데이터를 부모에서 자식으로 줄때 문제가 생긴다. 이런 문제를 아래 코드를 예시로 해결하자.

 

 

비동기적인 상황에서

부모가 자식에게 데이터를 내려주고 부모 컴포넌트가 바뀐데이터를 받는 방법

부모 컴포넌트

<template>
<date-picker
:propDate="dateData"
v-on:update:dateSettting="setDate"
/>
</template>
<script>
export default {
  data () {
    return {
    	dateData: '' // 비동기 데이터
    }
  },
  methods: {
    get () { // 비동기호출데티어
        http
          .get('sampleUrl')
          .then(response => {
            this.dateData = response.data
          })
          .catch(e => {
           
          })
          .finally(() => {
          })
      }
    },
    setDate (value) { // 이벤트버스 호출시 실행
      this.dateData = value
    },
  }  
</script>

자식 컴포넌트

<template>
  <div >
    <datepicker
      class="datepicker"
      placeholder="예) 2019-07-27"
      :language="languages['ko']"
      format="yyyy-MM-dd"
      ref="openDate"
      v-model="date"
    />
  </div>
</template>

<script>
import Datepicker from 'vuejs-datepicker/dist/vuejs-datepicker.esm.js'
import * as lang from 'vuejs-datepicker/dist/locale'

export default {
  props: {
    propDate: String
  },
  data () {
    return {
      languages: lang
      // date: this.propDate // 하위 컴포넌트부터 값이 정해지므로 초기렌더링시 값이 안내려온다
    }
  },
  computed: {
    date: {
      get () {
        console.log('get')
        return this.propDate
      },
      set (newVal) {)
        this.$emit('update:dateSettting', newVal)
      }
    }
  },
  components: {
    Datepicker
  }
}
</script>

 

부모가 먼저 생기지만 마운팅 순서는 자식이 먼저이기 때문에 초기 렌더링시 값이 비어있다.

이러한 이유로 위 코드에서 처럼 자식에 props 데이터를 자식 데이터에 연결해서 쓸때 data() 속성을 사용하지 않고,

computed 를 통해서 사용해야 한다.

 

이런 비동기적인 props를 전달받는 상황이라면 computed속성을 사용하는 것이 맞다

 

참고문헌

https://kjwsx23.tistory.com/357
[Vue.js] props로 받은 데이터를 data로 사용하기

https://stackoverflow.com/questions/45943682/how-to-initialize-data-properties-with-prop-values
How to Initialize Data Properties with Prop Values

 

반응형

+ Recent posts