본문 바로가기
DEV/Web 개발

Web 개발 :: 파이썬 Django Rest Framework(7) _ 회원가입, 로그인(Front-End)

by 올커 2022. 10. 31.

DRF(Django Rest Framework)(7) _ 회원가입 기능(Front-End)

01. 회원가입 기능 만들기

 - 이제 Front-End 부분까지 연결하여 회원가입 기능을 완성해보려 한다.

 - 아래 window.onload는 자바스크립트가 제대로 참조되어 시행되었는지 검사(F12)창의 콘솔에서 확인하고자 할 때 유용하게 사용할 수 있다.

window.onload = ()=>{
    console.log("로딩되었음")
}

 - 아래와 같이 인덱스 페이지를 만들고 자바스크립트를 활용하여 회원가입시 입력된 내용을 POST 해보려한다.

<!-- index.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Signup</title>
    <script src="api.js"></script>
</head>
<body>
    <h1>회원가입 페이지</h1>
    <form>
        <input type="email" name="email" id="email" placeholder="email">
        <input type="password" name="password" id="password" placeholder="password">
        <button type="button" onclick="handleSignin()">제출</button>
    </form>
</body>
</html>

 

 - 자바스크립트는 api.js로 만들고 아래와 같이 코드를 짰다. 

    window.onload는 api.js가 정상적으로 로드되었는지 콘솔창에서 확인하기 위함이고,

    async 이후는 email과 password를 각 input에서 value를 받아와서 const로 지정된 변수에 담아넣고, console에 확인하기 위해 log를 찍어준 부분이다.

    아래 const response 부분은 fetch된 부분이 모두 로드될 때까지 기다리고, POST 요청이 왔을 때 headers의 속성으로 body에 값을 넣어 response에 할당한다. 이때 body는 Json형식으로 입력되는데 string으로 바꾸어주기 위해 JSON.stringify 처리 해준다.

// api.js

window.onload = ()=>{
    console.log("로딩되었음")

}

async function handleSignin(){
    const email = document.getElementById("email").value
    const password = document.getElementById("password").value
    console.log(email, password)


    const response = await fetch('http://127.0.0.1:8000/users/signup/', {
        headers:{
            'content-type' : 'application/json',
        },
        method:'POST',
        body:JSON.stringify({
            "email":email,
            "password":password
        })
    })

    console.log(response)
}

 - 그러나 현재까지 진행된 부분을 실행해보면 콘솔창에서 아래와 같은 오류를 확인할 수 있다.

 - 잘 확인해보면 이전에 다루었던 CORS 문제인 것 같다. (※ 참고 링크)

  *CORS : Origin(주소와 포트)이 다를 경우 보안을 위해 Access 허용을 받도록 하는 것

  django-cors-headers를 설치하고,

pip install django-cors-headers

  아래 내용을 settings.py에 추가해준다.

# settings.py

INSTALLED_APPS = [
    ...,
    "corsheaders",
    ...,
]

MIDDLEWARE = [
    ...,
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    ...,
]

CORS_ALLOW_ALL_ORIGINS = True

 - 수정을 한 뒤에는 다시 서버를 켜주고, email, password를 입력 후 제출하면 아래와 같이 정보를 확인할 수 있다.

   db에서도 정상적으로 회원등록되어있음을 확인할 수 있다.


02. 로그인 기능 만들기

 - 이미 만들어진 회원가입 페이지를 활용하여 간단하게 로그인 페이지를 아래와 같이 만들었다.

   타이틀과 내용을 변경하고, 버튼의 onclick부분을 handleLogin으로 이름을 변경하였다.

<!-- login.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <script src="api.js"></script>
</head>
<body>
    <h1>로그인 페이지</h1>
    <form>
        <input type="email" name="email" id="email" placeholder="email">
        <input type="password" name="password" id="password" placeholder="password">
        <button type="button" onclick="handleLogin()">제출</button>
    </form>
</body>
</html>

 - 이제 api.js 파일에 아까 지정한 handleLogin을 추가해주어야 한다.

   마찬가지로 handleSignin()과 대부분 유사한 로직으로 동일하게 작성될 수 있어 가져왔지만, fetch 주소는 'http://127.0.0.1:8000/users/api/token/' 으로 변경해준다. (*django user app의 urls.py 참고)

// api.js

async function handleLogin(){
    const email = document.getElementById("email").value
    const password = document.getElementById("password").value
    console.log(email, password)

    const response = await fetch('http://127.0.0.1:8000/users/api/token/', {
        headers:{
            'content-type' : 'application/json',
        },
        method:'POST',
        body:JSON.stringify({
            "email":email,
            "password":password
        })
    })

    console.log(response)

 - 로그인 페이지에서도 정상적으로 읽어오고 있다. 그런데 우리는 토큰값을 알고 싶다.

 - 위의 코드에서 아랫줄에 'const response_json = await response.json()'를 추가해주고, 콘솔에 나타내기 위해console.log(response_json)로 변경해주면 토큰값을 읽어올 수 있게된다.

async function handleLogin(){
    const email = document.getElementById("email").value
    const password = document.getElementById("password").value
    console.log(email, password)

    const response = await fetch('http://127.0.0.1:8000/users/api/token/', {
        headers:{
            'content-type' : 'application/json',
        },
        method:'POST',
        body:JSON.stringify({
            "email":email,
            "password":password
        })
    })

    const response_json = await response.json()

    console.log(response_json)

}

 - 이제 토큰까지 잘 불러왔으니 불렁온 내용을 Local Storage에 저장하도록 한다. 현재는 검사(F12)창의 Application에 들어가보면 local storage에는 key, value가 아무것도 없음을 볼 수 있다.

 - local storage에 값을 저장하기 위해서는 localStorage.setItem("key", value값) 형식으로 저장할 수 있다.

   access와 refresh를 각각 저장하기 위해 아래와 같이 추가해준다.

// api.js

async function handleLogin(){

	...
    localStorage.setItem("access", response_json.access);
    localStorage.setItem("refresh", response_json.refresh);
}

 - 당연하다는 듯이 잘 저장되어 나타나는 것을 확인할 수 있다.

 - jwt에 들어가면 확인할 수 있는 payload 값도 local storage에 저장하고 싶을 때에는 아래 코드를 추가해준다.

async function handleLogin(){

	...
    const base64Url = response_json.access.split('.')[1];
    const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));

    localStorage.setItem("payload", jsonPayload)


}

 - payload도 정상적으로 local storage에 저장하였다.


03. 로그인 후 사용자 정보 인덱스 페이지에 로드하기

 - 기존에 index.html에 있던 내용은 회원가입 기능이므로 signup.html을 생성하여 코드를 옮겨주었다.

   index.html과 index.js에는 아래와 같이 작성했다.

<!-- index.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>홈페이지</title>
    <script src="index.js"></script>
</head>
<body>
    <h1>홈페이지</h1>
    <div id="intro"></div>

</body>
</html>

 - index.js 파일에서는 앞서 가져왔던 payload를 받아와 const로 저장 후 console.log로 출력해본다.

// index.js

console.log("로딩되었습니다.")

window.onload = ()=>{
    const payload = localStorage.getItem("payload");
    console.log(payload)
}

 - 위와 같이 작성 후 인덱스 페이지를 로드하면 아래와 같이 확인할 수 있다.

 - 하지만 이 상태에서는 특정 value를 바로 가져올 수 없다. payload 변수를 JSON.parse를 통해 처리하여 payload_parse라는 변수를 만들어주고, 출력해보면 JSON으로 분리된 것을 확인할 수 있다. 

// index.js

console.log("로딩되었습니다.")

window.onload = ()=>{
    const payload = localStorage.getItem("payload");
    const payload_parse = JSON.parse(payload)
    console.log(payload_parse)
}

 - 이제 특정 value를 꺼내올 수 있다.

// index.js

console.log("로딩되었습니다.")

window.onload = ()=>{
    const payload = localStorage.getItem("payload");
    const payload_parse = JSON.parse(payload)
    console.log(payload_parse.email)
}

 


04. 가져온 사용자 정보를 화면에 띄우기

 - 홈페이지에 intro라는 id를 가진 div를 만들었었다. 이를 활용하여 javascript에서 정보를 가져와 intro에 띄울 예정이다.

   아래 코드처럼 const intro를 만들고, document.getElementById("intro")를 통해 html파일에서 intro를 연결한다. 이 때 document는 html 파일을 지칭한다. 그 후 innerText를 사용하여 payload_parse.email을 할당하여 intro에 넣는다.

// index.js

console.log("로딩되었습니다.")

window.onload = ()=>{
    const payload = localStorage.getItem("payload");
    const payload_parse = JSON.parse(payload)
    console.log(payload_parse.email)

    const intro = document.getElementById("intro")
    intro.innerText = payload_parse.email
}

05. 가져온 사용자 정보를 화면에 띄우기

 - users 앱의 views.py에 아래와 같이 mockView를 만들어준다.

# users/views.py

class mockView(APIView):
    permission_classes = [permissions.IsAuthenticated]
    def get(self, request):
        user = request.user
        user.is_admin = True
        user.save()
        return Response("get 요청")

 - mock api기능을 적용하기 위해 로그인 화면에 아래와 같이 mock버튼을 추가한다.

<!-- login.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <script src="api.js"></script>
</head>
<body>
    <h1>로그인 페이지</h1>
    <form>
        <input type="email" name="email" id="email" placeholder="email">
        <input type="password" name="password" id="password" placeholder="password">
        <button type="button" onclick="handleLogin()">제출</button>
    </form>

    <button type="button" onclick="handleMock()">모크 api</button>
</body>
</html>

 - api.js파일에는 handleMock 함수를 아래와 같이 만들어준다. 위에서 정의한 mockView를 참고한다. (GET)

// api.js

async function handleMock(){
    const response = await fetch('http://127.0.0.1:8000/users/mock/', {
        headers:{
            "Authorization":"Bearer " + localStorage.getItem("access")
        },
        method: 'GET',
    })

    console.log(response)
}

 - 로그인 페이지에서 로그인(제출 버튼)을 하고 모크 api 버튼을 클릭하면 콘솔창에서 토큰을 확인할 수 있다.

 


06. 로그아웃 기능 구현하기

 - 로그아웃은 local storage에 있는 사용자 정보(access, refresh, payload)를 지워주는 개념이다.

 - 먼저 템플릿의 맨 아래쪽에 동작시킬 logout 버튼을 만들어준다.

<!-- login.html -->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <script src="api.js"></script>
</head>
<body>
    <h1>로그인 페이지</h1>
    <form>
        <input type="email" name="email" id="email" placeholder="email">
        <input type="password" name="password" id="password" placeholder="password">
        <button type="button" onclick="handleLogin()">제출</button>
    </form>

    <button type="button" onclick="handleMock()">모크 api</button>
    <button type="button" onclick="handleLogout()">로그아웃</button>
</body>
</html>

 - api.js 파일에는 logout 기능을 구현할 코드를 짜주면 된다. 로컬 스토리지에 저장된 정보를 삭제할 때에는 localStorage.removeItem("<삭제할 key명>")를 사용한다.

// api.js

...
async function handleLogout(){
    localStorage.removeItem("access")
    localStorage.removeItem("refresh")
    localStorage.removeItem("payload")
}

※ removeItem 대신에 clear()로 간단히 사용도 가능하다.

async function handleLogout(){
    localStorage.clear();
}

 

반응형

댓글