장고가 GraphQL 엔트포인트와 GraphiQL 인터페이스를 제공하도록하기위해 새로운 URL 패턴을 'backend/urls.py'에 추가한다. 이 URL 패턴은 Graphene-Django의 'GraphQLView'를 가리킨다.
# backend/urls.py
from django.contrib import admin
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from graphene_django.views import GraphQLView
urlpatterns = [
path("admin/", admin.site.urls),
path("graphql/", csrf_exempt(GraphQLView.as_view(graphiql=True))),
]
스키마는 각 Django 모델과 관련된 여러 클래스와 프론트엔드에서 필요한 몇 가지 중요한 쿼리 유형을 해결하는 방법을 지정하는 클래스로 구성된다.
# back_end/blog/schema.py
from django.contrib.auth import get_user_model
from graphene_django import DjangoObjectType
from blog import models
이제 각 모델과 사용자 모델에 해당하는 클래스를 만든다. 각 클래스는 GraphQL 타입을 나타내므로 이름끝에 'Type'을 붙인다.
# back_end/blog/schema.py
class UserType(DjangoObjectType):
class Meta:
model = get_user_model()
class AuthorType(DjangoObjectType):
class Meta:
model = models.Profile
class PostType(DjangoObjectType):
class Meta:
model = models.Post
class TagType(DjangoObjectType):
class Meta:
model = models.Tag
추가적으로 'graphene.ObjectType'을 상속하는 'Query' 클래스를 만들어야한다. 이 클래스는 생성한 모든 타입 클래스를 하나로 모아준다. 'graphene'을 가져와서 쿼리할 모델을 지정하는 메소드를 추가한다.
# back_end/blog/schema.py
import graphene
from django.contrib.auth import get_user_model
from graphene_django import DjangoObjectType
from blog import models
class Query(graphene.ObjectType):
all_posts = graphene.List(PostType)
author_by_username = graphene.Field(AuthorType, username=graphene.String())
post_by_slug = graphene.Field(PostType, slug=graphene.String())
posts_by_author = graphene.List(PostType, username=graphene.String())
posts_by_tag = graphene.List(PostType, tag=graphene.String())
def resolve_all_posts(root, info):
return models.Post.objects.prefetch_related("tags").select_related("author").all()
def resolve_author_by_username(root, info, username):
return models.Profile.objects.select_related("user").get(user__username=username)
def resolve_post_by_slug(root, info, slug):
return models.Post.objects.prefetch_related("tags").select_related("author").get(slug=slug)
def resolve_posts_by_author(root, info, username):
return models.Post.objects.prefetch_related("tags").select_related("author").filter(author__user__username=username)
def resolve_posts_by_tag(root, info, tag):
return models.Post.objects.prefetch_related("tags").select_related("author").filter(tags__name__iexact=tag)
'Query'클래스는 'graphene.List' 또는 'graphene.Field' 유형의 여러 속성으로 구성된다. 단일 항목을 반환하는 쿼리에는 'graphene.Field'를 사용하고, 여러 항목을 반환하는 쿼리에는 'graphene.List'를 사용한다.
이제 모든 타입과 resolver를 가진 스키마가 완성되었다.
# back_end/blog/schema.py
schema = graphene.Schema(query=Query)
이제 서버를 실행하여 GraphQL 인터페이스에 접속하자.
python manage.py runserver
브라우저를 열고 localhost:8000/graphql 주소로 이동하면 다음과 같은 화면이 뜬다.

여기에서 GraphiQL 인터페이스를 사용하여 GraphQL 쿼리를 실행 할 수 있다. 또한 저장된 데이터에 접근하고 다양한 쿼리를 테스트 할 수 있다.
이제 화면 좌측 상단의 Docs 섹션을 확장하고 'query:Query'를 클릭한다. 이제 다음 쿼리를 입력해보자! 다음 쿼리는 생성한 모든 게시물의 리스트를 반환한다.
{
allPosts {
title
author {
user {
username
}
}
tags {
name
}
}
}
위의 쿼리를 입력 후 play 버튼을 누르면 게시물 목록을 반환받는다.

백엔드와 프론트엔드가 로컬에서 다른 포트에서 실행이 되기때문에, 프로덕션 환경에서 완전히 별개의 도메인에서 실행 될 수 있기 때문에 교차 출처 리소스 공유(Cross-origin resource sharing, CORS)를 처리해야한다. 이를 처리하지 않으면 프론트엔드에서 백엔드로의 요청이 브라우저에 의해 차단된다. django-cors-headers 프로젝트를 사용하면 CORS를 쉽게 처리 할 수 있다. 이 프로젝트를 사용하여 Django가 다른 출처에서 오는 요청에도 응답하도록 설정하면 프론트엔드가 GraphQL API와 원활하게 통신 할 수 있다.
교차 출처 리소스 공유(CORS)란?
추가 HTTP 헤더를 사용하여 한 출처에서 실행중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근 할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행한다.
'django-cors-headers'를 requirements 파일에 추가한다.

이제 업데이트된 requirements 파일을 사용하여 패키지를 설치한다.
yeryeongseo@seoyelyeong-ui-MacBookPro backend % python3 -m pip install -r requirements.txt
이제 프로젝트의 'settings.py' 모듈에서 'INSTALLED_APPS' 리스트에 'corsheaders'를 추가한다.

저장하기전에 'MIDDLEWARE'리스트의 맨 앞에 "corsheaders.middleware.CorsMiddleware" 을 추가한다. django-cors-headers 문서에서는 미들웨어 리스트의 가능한 앞 쪽에 이 미들웨어를 두는것을 권장한다고한다. 최소한 Django의 'CommonMiddleware'보다 앞에 두어야한다.

이제 django-cors-headers 를 설치해보자
yeryeongseo@seoyelyeong-ui-MacBookPro back_end % pip3 install django-cors-headers
특정 도메인에서만 요청을 허용하려면 settings.py 에 다음을 추가해준다.
CORS_ALLOWED_ORIGINS = [
"http://localhost:5173",
]
이제 Django 백엔드가 CORS 설정을 통해 프론트엔드에서의 요청을 허용 할 수 있다.
공식홈페이지에서 다운받아준다.

설치가 확인되었다면 Vue 프로젝트를 생성할 디렉토리 'dvg'로 이동해 생성한다.

Project name(프로젝트 이름): front_end
Add TypeScript?(TypeScript를 추가하시겠습니까?): No
Add JSX Support?(JSX 지원을 추가하시겠습니까?): No
Add Vue Router for Single Page Application development?(단일 페이지 애플리케이션 개발을 위해 Vue Router를 추가하시겠습니까?): Yes
Add Pinia for state management?(상태 관리를 위해 Pinia를 추가하시겠습니까?): No
Add Vitest for Unit Testing?(단위 테스트를 위해 Vitest를 추가하시겠습니까?): No
Add an End-to-End Testing Solution?(종단 간 테스트 솔루션을 추가하시겠습니까?): No
Add ESLint for code quality?(코드 품질을 위해 ESLint를 추가하시겠습니까?): No
Add Vue DevTools 7 extension for debugging?(디버깅을 위해 Vue DevTools 7 확장을 추가하시겠습니까?): Yes(그냥 Yes해봄)
이제 백엔드 폴더, 프론트엔드 폴더가 만들어진걸 확인 할 수 있다. 
front_end/ 폴더로 이동하여 프로젝트의 모든 종속 항목을 설치한다.

프로젝트 구조가 마련되고 모든 종속 항목이 설치되었으므로 Vite 개발 서버를 실행 할 수 있다.

$ npm run dev

기본적으로 Vite 서버는 5173 포트에서 실행된다. 이제 Django 애플리케이션은 http://localhost:8000 에서 실행되고 Vue 애플리케이션은 http://localhost:5173 에서 실행된다. http://localhost:5173를 방문해보자.
야호