연동을 위해 백엔드와 프론트엔드 두 서버를 모두 키도록한다. 당연히 포트번호는 달라야 한다.
이전 포스팅에서 가정한 조건들을 일단 정적인 변수로 부여하고 연결의 목적만 달성해보도록 한다.
from rest_framework.views import APIView
from rest_framework.response import Response
rest_framwork로부터 APIView와 Response를 임포트 해주고, DataView class를 제작하고 APIView 클래스를 상속시켜준다. 어차피 복잡한 계산은 score_accesibillity함수가 수행하므로 우리는 최종결과인 score만 얻도록한다. 이것을 Response()로 띄운다. 이렇게 하고 웹을 확인해보면 어떤 플랫폼같은 것이 등장한다.(오오...)
물론 라우터도 수정해주어야 한다.
줄 준비를 완료한 장고 프로젝트는 잠깐 내려주고, Vue로 간다. Vue에서 요청을 보내기 위해 axios 패키지를 npm install axios로 완료해주고, 정석적으로 Get 요청을 작성한다. 알아볼 수 있게 로그만 추가해준다. onClickGetData()로 따로 메서드를 구성한 이유는 클릭 이벤트로 전달할 것이기 때문이다.
다음과 같이 Start버튼을 누르면 data : 에 백엔드에서 연산된 결과가 담기도록 해야한다.
하지만 역시나 에러가 뜨고 확인해보니 CORS문제였다. 찾아보니 장고에서 친절하게 이런 해결법을 제시해주었다.
쭉쭉 읽어가며 필요한 내용을 모두 setting.py에 반영한다.
# setting.py
"""
Django settings for semose project.
Generated by 'django-admin startproject' using Django 4.2.7.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-&)ict6eqv8sw$up!j5z*hai03gv01@33%cq2rz@sl0!6)v%7bs'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api', # 추가
'rest_framework', # 추가
'corsheaders', # 추가
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware', # 추가
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'semose.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'semose.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'Semose_DB',
'USER': 'root',
'PASSWORD': 'apdlvmf4@@',
'HOST': 'jkkyui-MacBookPro.local', # MySQL 서버 호스트
'PORT': '3306', # 기본 포트 (3306)
}
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
##### CORS 추가 #####
CORS_ALLOWED_ORIGINS = [
"https://example.com",
"https://sub.example.com",
"http://localhost:8000",
"http://localhost:8080",
]
CORS_ALLOW_METHODS = [
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
]
CORS_ALLOW_HEADERS = (
"accept",
"authorization",
"content-type",
"user-agent",
"x-csrftoken",
"x-requested-with",
)
그리고 다시 START버튼 클릭.
성공!! 프론트엔드에서 요청을 날리고 백엔드단에서 구성된 여러 로직에 의해 계산된 결과를 다시 프론트엔드로 받아 브라우져에 띄우는 한 싸이클을 구현할 수 있었다.
이제는 해당 점수가 나오도록 정적인 변수로 구성한 부분을 프론트엔드에서 사용자들이 입력하는 것으로 데이터를 POST 해보도록 한다.
첫번째로 주소입력기능을 구현한다. 메인 페이지의 하위 컴포넌트로 다음과 같이 구현하였다.
<template>
<div>
<b-form-input v-model="inputaddress"
placeholder="주소를 입력하세요." >
</b-form-input>
<b-col class="pb-2" >
<b-button size="lg"
@click='transferEvent'
> 주소입력 완료
</b-button>
</b-col>
</div>
</template>
<script>
export default {
name: 'AddressComponent',
props: {
},
data() {
return {
inputaddress: '' // 로컬 데이터로 사용할 변수
}
},
methods: {
transferEvent: function() {
console.log('updateAddress')
this.$emit('onUpdateAddress',this.inputaddress); // input 이벤트를 이용하여 주소를 부모 컴포넌트로 전달
}
}
}
</script>
<style>
</style>
다음과 같이 부트스트랩의 input 컴포넌트 양식으로 하여금 입력 후 주소입력 완료를 누르면 data단으로 전달되게끔 했다.
뭔가 짜치는 느낌도 들고, 이상하게 입력했을 때 예외 사항을 만드는 것이 너무 귀찮아 알아보던중 카카오에서 좋은 오픈소스를 주었다. 만들어둔거니 일단 저장하고, 새로 주소컴포넌트를 만들었다.
<template>
<div>
<!-- 우편번호 입력 버튼 -->
<button @click="execDaumPostcode">우편번호 찾기</button>
<!-- 우편번호 표시 -->
<p>우편번호: {{ postcode }}</p>
<!-- 주소 표시 -->
<p>주소: {{ address }}</p>
<!-- 참고 주소 표시 -->
<p>참고 주소: {{ extraAddress }}</p>
</div>
</template>
<script>
export default {
data() {
return {
postcode: "",
address: "",
extraAddress: "",
};
},
methods: {
execDaumPostcode() {
new window.daum.Postcode({
oncomplete: (data) => {
if (this.extraAddress !== "") {
this.extraAddress = "";
}
if (data.userSelectedType === "R") {
// 사용자가 도로명 주소를 선택했을 경우
this.address = data.roadAddress;
} else {
// 사용자가 지번 주소를 선택했을 경우(J)
this.address = data.jibunAddress;
}
// 사용자가 선택한 주소가 도로명 타입일때 참고항목을 조합한다.
if (data.userSelectedType === "R") {
// 법정동명이 있을 경우 추가한다. (법정리는 제외)
// 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
if (data.bname !== "" && /[동|로|가]$/g.test(data.bname)) {
this.extraAddress += data.bname;
}
// 건물명이 있고, 공동주택일 경우 추가한다.
if (data.buildingName !== "" && data.apartment === "Y") {
this.extraAddress +=
this.extraAddress !== ""
? `, ${data.buildingName}`
: data.buildingName;
}
// 표시할 참고항목이 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
if (this.extraAddress !== "") {
this.extraAddress = `(${this.extraAddress})`;
}
} else {
this.extraAddress = "";
}
// 우편번호를 입력한다.
this.postcode = data.zonecode;
this.$emit("onUpdateAddress", this.address);
},
}).open();
},
},
};
</script>
<style>
</style>
우편번호 찾기를 누르면
다음과 같이 익숙한 포맷이 나타난다. 여기서 검색후 창이 닫히면 부모 컴포넌트인 mainpage컴포넌트의 데이터인 address에 저장이 되도록 구현했다.
이 address를 post를 통해 백엔드로 전달해야 한다. 그리고 시각적 생산성을 고려해 입력시 주소가 입력되었음을 알리는 화면 단에서의 기능을 추가해야한다. 다음장에서~
덕분에 몇일 헤매면서 안되었던 거 해결했습니다~
감사합니다.