중간 점검
- SideBar 추가
- Toast 추가
- 복무 기간 페이지 (’/army’) 완성
- vue-chart-3 사용
- vue-chartjs를 사용하려 했으나 vue 3 를 지원하지 않는 관계로 feature가 적으나 호환이 되는 vue-chart-3를 선택
- 로그인 페이지 구현
백엔드 개발
백엔드는 유일하게 사용할 줄 아는 django rest framework를 사용하기로 했다. 웹에서 새로운 걸 공부하기 위해 vue를 사용한 것과는 별개로 기존에 아는 것을 사용하기로 했는데 가장 큰 이유는 학습 곡선의 문제였다. 프론트가 html + js + css 의 큰 틀을 유지하면서 사용 방법만 차이가 있는 것과는 달리 백엔드 프레임워크들은 그 구성부터가 차이가 났기 때문이다. 따라서 새로운 프레임워크를 공부하며 개발을 하는 것 보다는 기존에 많이 사용해왔던 django를 사용하는 것이 더 효율적이라고 판단하였다.
가장 먼저 백엔드에서 진행했던 부분은 user 저장과 관련된 부분이었다. Username을 닉네임으로 사용하고자 했기 때문에 CustomUser을 만드는 방식을 채택했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils import timezone
from .managers import CustomUserManager
class CustomUser(AbstractBaseUser, PermissionsMixin):
user_id = models.CharField(max_length=30, unique=True, null=False)
username = models.TextField(max_length=30, blank=False, unique=True)
introduction = models.TextField(max_length=500, blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'user_id'
REQUIRED_FIELDS = ['username']
objects = CustomUserManager()
def __str__(self):
return f'User {self.user_id}'
위와 같이 AbstractBaseUser
을 상속해서 CustomUser
클래스를 생성해주고, admins.py
, managers.py
등 여러 파일을 적절히 수정한 뒤, settings.py
에서
1
AUTH_USER_MODEL = 'user.CustomUser'
와 같이 설정해주었다.
유저 인증 방식은 Token Authentication 방식을 사용했다. 서버에서는 로그인 시, Response body에 token을 포함시켜서 response하면 되며, 클라이언트에서는 Request를 보낼 때 Header에 token을 포함시키는 방식으로 작동한다.
1
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
이후 적절히 views.py
를 건드려서 로그인과 회원가입을 완성시켜 주었다.
Frontend + Backend
이제 프론트엔드와 백엔드가 잘 연결되는지를 확인해볼 시간이다.
Proxy 설정
먼저 Vue 선에서 proxy를 설정해주어야 하는데 기본적으로 vue 개발 서버에서 request를 보낼 때, 따로 설정하지 않으면 localhost:8080
으로 request를 보내게 된다. 우리의 백엔드 서버는 localhost:8000
에서 돌아가고 있으므로 vue project의 최상단에 vue.config.js
파일을 만들어 다음과 같이 설정해주었다.
1
2
3
4
5
6
7
8
9
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8000'
}
}
}
}
이와 같이 설정하면 클라이언트에서 api로 시작하는 요청이 localhost:8080
이 아니라 localhost:8000
으로 리다이렉트되어 요청된다.
axios
통신을 위해서 axios를 사용하였다. axios는 vuex와 연동하여 그 구조를 깔끔하게 만들어줄 수 있다.
vuex는 크게 state, getters, mutations, actions로 나눌 수 있다. 통신의 control flow를 글로 적어보면, 첫 번째로 Vue의 Component에서 actions 내의 async fuction(axios 포함)을 dispatch 한다. 이 async function은 axios를 활용하여 통신을 진행하게 되고, 그 결과를 가지고 mutation 내의 function을 commit 한다. 이후 mutation 내의 function이 state를 변경하게 되는 것이다.
실제 코드를 통해 간단하게 살펴보면 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Login.vue
export default {
name: 'Login',
data() {
return {
userId: '',
password: '',
}
},
methods: {
onClickLogin() {
const userData = {
user_id: this.userId,
password: this.password,
}
this.$store.dispatch('user/login', userData)
}
}
}
onClickLogin
에서 userId
와 password
정보를 userData
로 묶어주고, dispatch
를 통해 action을 불러온다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// store/user.js
const actions = {
async login({ commit }, loginData) {
axios.post('user/login/', loginData)
.then((res) => {
console.log(res.data);
const userData = {
user_id: res.data.user_id,
username: res.data.username,
introduction: res.data.introduction,
}
commit('updateUserData', userData);
commit('updateAccessToken', res.data.token);
})
}
}
이후 action은 axios를 통해 post request를 백엔드에 보내게 되고, 받은 result를 가지고 local storage에 있는 userData
와 accessToken
을 갱신시키는 mutation을 commit
을 통해 불러온다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// store/user.js
const state = () => ({
userData: {},
accessToken: '',
})
const mutations = {
updateUserData(state, userData) {
state.userData = userData;
},
updateAccessToken(state, accessToken) {
state.accessToken = accessToken;
}
}
결과적으로 mutation이 state를 변경시키면서 login flow가 끝이 난다.
이후 이렇게 저장된 userData
와 accessToken
을 활용하여 원하는 기능을 구현하면 된다.
결과
성공적으로 프론트엔드와 백엔드를 연결한 모습을 확인할 수 있다.
회원가입 페이지, 게시판 페이지 등 다양하게 할 일이 많이 남았다. 게시판부터 만든다고 해놓고 뭐가 이렇게 오래 걸리는지 모르겠다. 그래도 하나하나 새로운 것들을 배우며 진행하고 있기 때문에 큰 시간낭비라고 생각하진 않는다. 다음 게시물에선 진짜 게시판을 완성해야겠다…