0405 - 0411

0405

CORS 이슈

API 서버를 1차 완성시키고 프론트엔드 서버에서 API를 호출하여 제대로 콘솔에 나타내는지 테스트를 진행하였다. 그 결과 아래와 같은 오류가 발생하였다.

Access to XMLHttpRequest at ‘http://localhost:8080/boards/lists’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

해석해보면 CORS 정책을 위반했기 때문에 발생하는 오류라는 것을 알 수 있다.

CORS

  • CORS는 Cross-Origin Resource Sharing의 줄임말로, 한국어로 직역하면 교차 출처 리소스 공유라고 해석할 수 있다.
  • 여기서 Origin은 protocol, host, port number을 합친 것을 의미한다.
  • 결국 다른 출처로 리소스를 요청해서 사용하기 위해서는 CORS 정책을 지켜야만 했다.

해결책

전역적으로 적용하기 위해 WebConfig 클래스에 addCorsMappings 메소드를 오버라이딩 하여 설정하였다.

@Override
public void addCorsMappings(CorsRegistry registry) {
    // 모든 uri에 대해 http://localhost:3000의 접근을 허용한다.
    registry.addMapping("/**")
            .allowedOrigins("http://localhost:3000");
}

0406

register 함수를 실행하여 api 통신 테스트

구조화를 시키고 제대로 프론트엔드 개발에 들어가기 전에 api를 호출하였을 때 제대로 데이터가 응답되는지 테스트하기 위해 기본적인 회원가입 api를 테스트하였다.

<!-- SignupFrom.vue -->
<template>
  <div class="main_loginBox">
    <input
      type="text"
      class="login_id"
      placeholder="아이디"
      v-model="member.memberId"
    />
    <input
      type="text"
      class="login_name"
      placeholder="이름"
      v-model="member.memberName"
    />
    <input
      type="password"
      class="login_pw"
      placeholder="비밀번호"
      v-model="member.password"
    />
    <input type="password" class="login_pw" placeholder="비밀번호 확인" />
    <button class="btn_signup" @click="signupMethod">회원가입</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      member: {
        memberId: "",
        memberName: "",
        password: "",
      },
    };
  },
  methods: {
    async signupMethod() {
      const result = await this.$store.dispatch("register", this.member);

      console.log(result);
    },
  },
};
</script>

<style scoped src="../../assets/css/member/form.css"></style>
  • member 객체의 속성을 memberId, memberName, password에 매치시켜서 저장하여 회원가입 버튼 클릭 시 store에 있는 register 메소드를 실행시키도록 하였다.
// member-module.js
/* eslint-disable no-unused-vars */
import memberService from "../service/memberService";

export const member = {
  state: {},
  actions: {
    async register({ commit }, member) {
      try {
        const result = await memberService.register(member);
        console.log(result);
      } catch (error) {
        console.log(error.response);
      }
    },
  },
};
  • axios는 비동기 통신이므로 actions에 선언하여 try catch문으로 처리하였다.
  • { commit }을 작성하고 사용하지 않는 경우에는 에러가 발생하는 데, 에러를 발생시키지 않기 위해 2번 째 줄에 주석을 작성하였다.
  • console.log(error.response);를 통해서 에러 시에도 정상적으로 데이터가 반환되도록 하였다.
// memberService.js
import axios from "axios";

const register = (member) => {
  console.log(member);
  const url = "http://localhost:8080/members/new";
  return axios.post(url, member);
};

export default { register };
  • api를 호출하는 로직은 모두 한 곳에 모아두기 위해서 service 디렉토리 밑에 작성하였다.
  • 다른 곳에서 메소드를 호출하기 위해 export default 처리하였다.

0407

mongoDB 소개

Document Database

  • 문서 지향 데이터베이스이다.
// field: value
{
  "name": "sue",
  "age": 26,
  "status": "A",
  "groups": ["news", "sports"]
}

장점

  • 문서는 많은 프로그래밍 언어 (ex 자바스크립트)에서 기본 데이터 타입으로 제공한다.
  • 내장된 문서(json 안에 json)와 배열은 조인의 필요성을 줄인다.
  • polymorphism 구현에 유리하도록 동적 스키마를 제공한다.

컬렉션/뷰/On-Demand Materialized Views

  • 몽고DB는 문서를 컬렉션에 저장한다.
  • 컬렉션은 관계 DB의 테이블과 같은 역할을 한다.

  • 컬렉션에서 몽고DB 지원:
    • 읽기 전용 뷰
    • 실체화된 뷰

몽고DB 주요 특징

고성능

  • 임베디드 데이터 모델을 지원해서 입출력에 관한 부담을 줄인다.
  • 인덱스는 빠른 속도의 쿼리를 지원한다.

풍부한 질의어

  • 몽고DB는 읽고 쓰기 연산을 지원하기 위해 풍부한 질의어를 제공한다.
    • 데이터 그룹
    • 텍스트 검색, 2차원 3차원 좌표 쿼리

가용성

  • replica set이라는 복제 기능을 제공한다.
    • 자동 복구
    • 데이터 중복성
  • replica set은 몽고DB 서버의 그룹으로 같은 데이터를 유지하고, 중복성을 제공하여 데이터 가용성을 높인다.

수평적 확장성

  • 몽고DB는 핵심 기능 중 하나로 수평적 확장성을 제공한다.
    • Sharding

다수의 Storage Engines을 제공

  • WiredTiger Storage Engine
  • In-Memory Storage Engine
  • 추가적으로, 몽고DB는 써드파티 storage engines을 허용하는 API를 제공한다.

0408

데이터베이스와 컬렉션

몽고DB는 데이터 레코드를 문서 형태(물리적으로는 BSON 문서)로 저장한다.

  • 데이터 베이스는 하나 이상의 컬렉션을 저장한다.
  • 컬렉션은 문서들의 집합이다.
  • 데이터베이스를 사용하기 위해서 선택하려면 use myDB 명령어를 사용한다.

create a Database

  • 만약 데이터베이스가 존재하지 않으면, 처음으로 데이터베이스에 데이터를 저장할 때 몽고DB가 데이터 베이스를 만들어준다.

Collections

  • 몽고DB는 문서를 컬렉션에 저장한다.
  • 컬렉션은 관계DB의 테이블과 비슷하다.
  • 컬렉션은 스키마 형태를 강제하지는 않지만, 일반적으로는 비슷하다.

create a Collection

  • 컬렉션이 없다면, 처음으로 컬렉션에 데이터를 저장할 때 몽고DB가 컬렉션을 만들어준다.

Documents

  • 데이터 모델로 문서를 사용한다.
  • 필드와 값 쌍으로 문서를 구성한다.
  • 다른 문서의 같은 일반 필드에서 데이터 타입이 달라도 허용한다.

필드 이름

  • _id는 기본 키로 사용된다.
  • 필드 이름은 null 형태를 포함할 수 없다.

RDBMS vs MongoDB

Database - Database
Table - Collection
Tuple/Row - Document
column - Field
Table join - Embedded Documents
Primary Key - Primary Key (Default key _id)

Database Server and client

Server mysqld/Oracle - mongod
client mysql/sqlplus - mongo

0409

vee-validate

VeeValidate는 Vue.js를 위해 특별히 구축 된 유효성 검사 프레임 워크로, 함수 기반 API 및 Vue 구성 요소의 모음이다.

Validation Provider

  • 먼저 필드에 대한 유효성 검사기 역할을 하는 구성 요소로 ValidationProvider를 컴포넌트 등록해야 한다.
import { ValidationProvider } from "vee-validate";

export default {
  components: {
    ValidationProvider,
  },
};
  • 그런 다음 구성 요소 템플릿에서 사용할 수 있으며, 일반적으로 input 태그를 래핑해서 사용한다.
<ValidationProvider v-slot="{ errors }">
  <input v-model="value" type="text" />
  <span></span>
</ValidationProvider>
  • 이제 에러 메세지를 화면에 보여줄 수 있다.

규칙 추가

VeeValidate는 번들 크기를 최대한 작게 유지하기 위해 기본적으로 유효성 검사 규칙과 함께 설치되지 않는다. 함수 API 중 첫 번째는 extend 함수이다.

import { extend } from "vee-validate";

extend("positive", (value) => {
  return value >= 0;
});
  • extend 함수는 해당 규칙에 사용할 규칙 이름과 유효성 검사기 함수를 받는다.
  • <ValidationProvider rules="positive" v-slot="{ errors }">와 같은 형식으로 새로 정의한 규칙을 사용할 수 있다.

0410

메시지

  • VeeValidate는 필드에 대한 오류 메시지를 생성한다.
  • 유효성 검사 함수 자체에서 문자열을 반환하여 변경할 수 있다.
extend("odd", {
  validate: (value) => {
    return value % 2 !== 0;
  },
  message: "This field must be an odd number",
});

이렇게 하면 validator 기능이 훨씬 더 명확해진다.

필드 이름 자리 표시

에러 메시지에 필드 이름을 포함하여 출력하고 싶은 경우

extend("positive", (value) => {
  if (value >= 0) {
    return true;
  }

  return "The {_field_} field must be a positive number";
});

와 같이 {field}를 메시지 안에 넣으면 ValidationProvider 태그의 name 속성에 해당하는 문자가 메세지에 입력된다.

규칙 가져오기

유효성 검사 규칙은 vee-validate/dist/rules를 import 하여 사용할 수 있다.

import { extend } from "vee-validate";
import { required, email } from "vee-validate/dist/rules";

// No message specified.
extend("email", email);

// Override the default message.
extend("required", {
  ...required,
  message: "This field is required",
});

0411

ValidationObserver

폼 전체의 유효성을 검사하는 것으로, 폼 안에 있는 필드의 유효성 검사 상태를 관찰한다.

  • ValidationObserver는 에러와 유효 플래그를 집계하여 모든 필드들의 유효 검사 상태를 나타낸다.

사용법

제출하기 전에 form의 유효성을 검사하는 것은 필수이다. ValidationObserver는 handleSubmit 함수를 제공하여 유효한 form을 제출한 후에만 핸들러를 실행한다. 아래는 모든 유효성 검사를 적용한 코드이다.

<template>
  <div class="main_loginBox">
    <ValidationObserver v-slot="{ handleSubmit }">
      <form @submit.prevent="handleSubmit(signupMethod)">
        <ValidationProvider
          name="아이디"
          rules="required|email"
          v-slot="{ errors }"
        >
          <input
            type="text"
            class="login_id"
            placeholder="아이디"
            v-model="member.memberId"
          />
          <span></span>
        </ValidationProvider>
        <ValidationProvider name="이름" rules="required" v-slot="{ errors }">
          <input
            type="text"
            class="login_name"
            placeholder="이름"
            v-model="member.memberName"
          />
          <span></span>
        </ValidationProvider>
        <ValidationProvider
          name="비밀번호"
          vid="password"
          rules="required|checkSpace|regex|min:10|max:32"
          v-slot="{ errors }"
        >
          <input
            type="password"
            class="login_pw"
            placeholder="비밀번호"
            v-model="member.password"
          />
          <span></span>
        </ValidationProvider>
        <ValidationProvider
          name="비밀번호 확인"
          rules="required|confirmed:password"
          v-slot="{ errors }"
        >
          <input
            type="password"
            class="login_pw"
            placeholder="비밀번호 확인"
            v-model="reEnterPassword"
            @keyup.enter="signupMethod"
          />
          <span></span>
        </ValidationProvider>
        <button type="submit" class="btn_signup">회원가입</button>
      </form>
    </ValidationObserver>
  </div>
</template>

<script>
import { mapActions } from "vuex";
import { extend, ValidationProvider, ValidationObserver } from "vee-validate";
import { required, email, min, max, confirmed } from "vee-validate/dist/rules";

extend("required", {
  ...required,
  message: "{_field_}은(는) 필수 입력 사항입니다.",
});

extend("email", {
  ...email,
  message: "{_field_}은(는) 이메일 형식으로 작성해야 합니다.",
});

extend("checkSpace", {
  validate: (value) => {
    if (value.search(/\s/) === -1) {
      return true;
    }
  },
  message: "공백은 입력이 불가능합니다.",
});

extend("regex", {
  validate: (value) => {
    if (
      // 정규 표현식을 만족한다면 true
      /^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]*$/.test(
        value
      )
    ) {
      return true;
    }
  },
  message: "영문과 숫자와 특수문자를 최소 1자 이상 입력해주세요.",
});

extend("min", {
  ...min,
  message: "{_field_}은(는) 최소 10자 이상 32자 이하로 입력해주세요.",
});

extend("max", {
  ...max,
  message: "{_field_}은(는) 최소 10자 이상 32자 이하로 입력해주세요.",
});

extend("confirmed", {
  ...confirmed,
  message: "비밀번호가 일치하지 않습니다.",
});

export default {
  components: {
    ValidationProvider,
    ValidationObserver,
  },
  data() {
    return {
      member: {
        memberId: "",
        memberName: "",
        password: "",
      },
      reEnterPassword: "",
    };
  },
  methods: {
    ...mapActions(["register"]),
    async signupMethod() {
      const result = await this.register(this.member);

      console.log(result);
    },
  },
};
</script>

<style scoped src="../../assets/css/member/form.css"></style>
  • ValidationProvider를 사용해 v-model에 연결된 값의 유효성을 검사한다.
  • ValidationObserver를 사용하기 위해 ValidationProvider를 form 태그로 감싸서 handleSubmit 메소드로 submit시 유효성을 검사해서 유효하지 않다면 api 호출을 할 수 없도록 막았다.

태그:

카테고리:

업데이트: