장고 내장 User객체를 사용하지 않고 로그인 로그아웃 기능을 만들었다.
그리고 로그인 로직에 대해서 고민하고 성능 향상을 했다.
로그인 기능을 구현해 보았다.
def login(request):
if 'user' in request.session:
return redirect('index')
if request.method == 'GET':
return render(request, 'accounts/login.html')
elif request.method == 'POST':
error_message = {}
userid = request.POST.get('user_id', None)
password = request.POST.get('password', None)
if not Accounts.objects.filter(user_id=userid).exists():
error_message['error'] = '등록되지 않은 아이디 입니다.'
return render(request, 'accounts/login.html', error_message)
user = Accounts.objects.get(user_id=userid)
if not check_password(password, user.password):
error_message['error'] = '비밀번호를 틀렸습니다.'
return render(request, 'accounts/login.html', error_message)
request.session['user'] = user.user_id
return redirect('index')
def login(request):
if 'user' in request.session:
return redirect('index')
if request.method == 'GET':
return render(request, 'accounts/login.html')
로그인 뷰가 실행 됐을 때 가장 처음으로 세션에 로그인이 되었는지 확인을 하게한다.
왜냐하면 프론트 단에서 로그인 상태이면 다시 로그인 페이지로 가는 바로가기가 안뜨고 로그아웃만 뜨게 만들거지만 사용자가 로그인 url을 알고 직접 접근할 수 있으므로 로그인url에 들어가면 맨 처음으로 세션을 확인해서 로그인 상태인지를 확인한다. 만약 로그인 상태면 index페이지로 리다이렉트 되게 했다.
로그인 상태가 아니며 GET요청으로 접근하면 로그인 html을 렌더링 해준다.
elif request.method == 'POST':
error_message = {}
userid = request.POST.get('user_id', None)
password = request.POST.get('password', None)
if not Accounts.objects.filter(user_id=userid).exists():
error_message['error'] = '등록되지 않은 아이디 입니다.'
return render(request, 'accounts/login.html', error_message)
user = Accounts.objects.get(user_id=userid)
if not check_password(password, user.password):
error_message['error'] = '비밀번호를 틀렸습니다.'
return render(request, 'accounts/login.html', error_message)
request.session['user'] = user.user_id
return redirect('index')
로그인 로직은 입력값을 Accounts모델에 검색해서 없으면 '등록되지 않은 아이디 입니다.' 메시지를 넘겨주고 값이 있으면 user변수에 해당하는 값의 객체를 담고 비밀번호를 확인하게 짰다.
역시나 짜면서도 뭔가 중복이 된다는 느낌을 받았고 이 포스팅을 쓰면서 문제점을 파악해보기로 했다.
일단 세션확인하는 쿼리 한번은 무조건 있고
그외 쿼리가 두번이나 실행이 된다.
그것은 필터로userid가 존재하는지 확인할 때 한번 쿼리가 날라가고
userid에 해당하는 객체를 변수에 담을 때 한번 날린다.
지금이야 0.3ms로 가능하지만 만약 회원수가 1000만개라면???? 행복회로 이지만
쿼리문을 두번이나 날리는 건 효율이 떨어지니 수정을 했다.
수정을 하면서 알게 됐지만 filter문에서 정보를 조회해서 컬럼값을 조회할 수 없어서 get을 써서 담으려고 했지만 get은 검색값이 없으면 에러값을 던져서 로직을 한번에 처리하는데 생각을 좀 했다.
elif request.method == 'POST':
try:
error_message = {}
userid = request.POST.get('user_id', None)
password = request.POST.get('password', None)
user = Accounts.objects.get(user_id=userid)
except:
error_message['error'] = '등록되지 않은 아이디 입니다.'
return render(request, 'accounts/login.html', error_message)
else:
if not check_password(password, user.password):
error_message['error'] = '비밀번호를 틀렸습니다.'
return render(request, 'accounts/login.html', error_message)
request.session['user'] = user.user_id
return redirect('index')
try except 문을 사용해서 user변수에 Accounts모델에서 입력받은 값을 get메소드로 조회하게 했다. 등록 된 아이디가 없으면 에러가 던져지므로 except문으로 '등록되지않은 아이디 입니다' 메시지를 넘겨준다.
그리고 else문에서 비밀번호를 체킹하는데 user.password는 쿼리문을 날리지 않는다.
쿼리문이 두개로 줄어들었다.
def logout(request):
if 'user' in request.session:
del request.session['user']
return redirect('index')
return redirect('index')