Fetch
JavaScript의 내장 함수로, 웹 브라우저에서 서버와 통신하기 위해 사용됩니다. 주로 HTTP 요청을 보내고 서버에서 데이터를 가져오는 데 사용됩니다. fetch는 특히 비동기 방식으로 작동하며, 웹 애플리케이션에서 API 호출을 구현할 때 가장 많이 사용되는 방법 중 하나입니다.
기본 개념
HTTP 요청: fetch는 HTTP 프로토콜을 사용하여 서버에 데이터를 요청하거나 데이터를 보냅니다. 예를 들어, GET, POST, PUT, DELETE 등의 요청을 처리할 수 있습니다.
Promise 기반: fetch는 Promise를 반환합니다. 비동기 작업을 쉽게 처리할 수 있도록 설계되었습니다.
JSON 지원: API 응답이 JSON 형식일 경우, 데이터를 쉽게 처리할 수 있도록 JSON 파싱을 지원합니다.
기본 회원가입 및 로그인 DRF 및 Fetch 구현
1. DRF: 회원가입 및 로그인 API 구현
회원가입 API
# serializers.py from rest_framework import serializers from django.contrib.auth.models import User class RegisterSerializer(serializers.ModelSerializer): password2 = serializers.CharField(write_only=True) class Meta: model = User fields = ['username', 'email', 'password', 'password2'] def validate(self, attrs): if attrs['password'] != attrs['password2']: raise serializers.ValidationError({"password": "Passwords do not match."}) return attrs def create(self, validated_data): validated_data.pop('password2') user = User.objects.create_user(**validated_data) return user
먼저, def create(self, validated_data)에서 validated_data는 무엇이냐고 하면,
validated_data는 Django Rest Framework (DRF)에서 제공하는 serializer의 동작 과정에서 자동으로 생성되고 전달되는 데이터입니다. 이 데이터는 serializer를 통해 클라이언트에서 제공된 입력 데이터를 검증하고 정리한 결과입니다.
즉, validated_data는 DRF의 serializer가 is_valid 호출 후 자동으로 생성합니다. 따라서 명시적으로 생성하지 않아도 DRF가 데이터 검증 과정을 통해 만들어주기 때문에, 개발자가 직접 생성할 필요가 없습니다.# views.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .serializers import RegisterSerializer class RegisterView(APIView): def post(self, request): serializer = RegisterSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response({"message": "User registered successfully!"}, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# views.py from rest_framework.authtoken.models import Token from django.contrib.auth import authenticate class LoginView(APIView): def post(self, request): email = request.data.get('email') password = request.data.get('password') user = authenticate(username=email, password=password) if user: token, _ = Token.objects.get_or_create(user=user) return Response({"token": token.key}, status=status.HTTP_200_OK) return Response({"error": "Invalid credentials"}, status=status.HTTP_401_UNAUTHORIZED)
# urls.py from django.urls import path from .views import RegisterView, LoginView urlpatterns = [ path('api/register/', RegisterView.as_view(), name='register'), path('api/login/', LoginView.as_view(), name='login'), ]
2. Frontend: fetch를 활용한 회원가입 및 로그인
회원가입 (Register)
아래는 Bootstrap 폼과 JavaScript fetch를 사용한 회원가입 요청 예제입니다.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Register</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container mt-5"> <h1>회원가입</h1> <form id="registerForm"> <div class="mb-3"> <label for="email" class="form-label">이메일</label> <input type="email" class="form-control" id="email" required> </div> <div class="mb-3"> <label for="username" class="form-label">유저네임</label> <input type="text" class="form-control" id="username" required> </div> <div class="mb-3"> <label for="password" class="form-label">비밀번호</label> <input type="password" class="form-control" id="password" required> </div> <div class="mb-3"> <label for="password2" class="form-label">비밀번호 확인</label> <input type="password" class="form-control" id="password2" required> </div> <button type="submit" class="btn btn-primary">회원가입</button> </form> <p id="registerMessage" class="mt-3"></p> </div> <script> document.getElementById('registerForm').addEventListener('submit', function(event) { event.preventDefault(); const email = document.getElementById('email').value; const username = document.getElementById('username').value; const password = document.getElementById('password').value; const password2 = document.getElementById('password2').value; fetch('http://127.0.0.1:8000/api/register/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email, username, password, password2 }), }) .then(response => response.json()) .then(data => { if (data.message) { document.getElementById('registerMessage').textContent = "회원가입 성공!"; } else { document.getElementById('registerMessage').textContent = "오류: " + JSON.stringify(data); } }) .catch(error => console.error('Error:', error)); }); </script> </body> </html>
여기서 하나하나 봅시다.
document.getElementById('registerForm').addEventListener('submit', function(event) { event.preventDefault();
1 . document
document는 Document Object Model (DOM)을 의미합니다. 웹 페이지의 HTML 문서와 그 구조를 자바스크립트에서 접근할 수 있도록 만들어주는 객체입니다. 이 객체를 통해 HTML 요소들을 찾고, 수정하고, 이벤트를 추가할 수 있습니다.
2. getElementById('registerForm')
getElementById는 DOM에서 특정 id를 가진 HTML 요소를 찾는 메서드입니다.
'registerForm'은 id 속성값입니다. 즉, 이 메서드는 HTML 문서 내에서 id="registerForm"인 요소를 찾아 반환합니다.
<form id="registerForm">...</form>
예를 들어 위와 같은 HTML 요소가 있다면, getElementById('registerForm')는 그 폼을 반환합니다.
3. addEventListener('submit', function(event) {...})
addEventListener는 이벤트 리스너를 등록하는 메서드입니다. 이 메서드는 특정 이벤트가 발생했을 때 호출될 함수(콜백 함수)를 지정합니다.
첫 번째 인자: 'submit'은 이벤트의 종류로, 여기서는 사용자가 폼을 제출하는 submit 이벤트입니다.
두 번째 인자: function(event)은 콜백 함수로, 이벤트가 발생했을 때 실행될 함수입니다.
즉, 이 코드는 사용자가 'registerForm'을 제출할 때마다 특정 동작을 실행하도록 이벤트 리스너를 추가하는 것입니다.
4. 'submit'
submit은 HTML 폼에서 사용되는 이벤트입니다. 사용자가 폼을 제출할 때 발생합니다. 사용자가 버튼을 클릭하거나 엔터 키를 눌러 폼을 제출하면 이 이벤트가 트리거됩니다.
5. function(event)
function(event)은 자바스크립트에서 익명 함수를 정의하는 부분입니다.
event는 이벤트 객체를 의미하며, submit 이벤트가 발생할 때 그와 관련된 모든 정보를 담고 있는 객체입니다. 예를 들어, 어떤 요소에서 이벤트가 발생했는지, 이벤트가 발생한 위치는 어디인지 등의 정보가 포함됩니다.
익명 함수는 이름이 없는 함수를 의미합니다. 코드에서 즉시 사용될 함수를 선언할 때 주로 사용됩니다.
6. event.preventDefault()
preventDefault()는 이벤트가 발생한 기본 동작을 막는 메서드입니다. 예를 들어, 폼이 제출되면 기본적으로 페이지가 새로고침되거나 리로드되는데, 이를 막고 자바스크립트로 직접 처리를 하기 위해 이 메서드를 사용합니다.
즉, event.preventDefault()는 폼 제출 시 페이지가 새로 고침되는 기본 동작을 막는 역할을 합니다. 이로 인해 페이지 리로드 없이 AJAX로 서버와 통신할 수 있습니다.const email = document.getElementById('email').value; const username = document.getElementById('username').value; const password = document.getElementById('password').value; const password2 = document.getElementById('password2').value; fetch('http://127.0.0.1:8000/api/register/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email, username, password, password2 }), }) .then(response => response.json()) .then(data => { if (data.message) { document.getElementById('registerMessage').textContent = "회원가입 성공!"; } else { document.getElementById('registerMessage').textContent = "오류: " + JSON.stringify(data); } }) .catch(error => console.error('Error:', error)); });
1. const: 자바스크립트에서 변수를 선언할 때 사용하는 키워드입니다. const는 상수(constant)를 선언할 때 사용됩니다. 즉, 한번 값을 할당한 후에는 그 값을 변경할 수 없다는 특성을 가집니다.
로그인 (Login)
로그인도 비슷한 방식으로 처리할 수 있습니다.
<div class="container mt-5"> <h1>로그인</h1> <form id="loginForm"> <div class="mb-3"> <label for="email" class="form-label">이메일</label> <input type="email" class="form-control" id="loginEmail" required> </div> <div class="mb-3"> <label for="password" class="form-label">비밀번호</label> <input type="password" class="form-control" id="loginPassword" required> </div> <button type="submit" class="btn btn-primary">로그인</button> </form> <p id="loginMessage" class="mt-3"></p> </div> <script> document.getElementById('loginForm').addEventListener('submit', function(event) { event.preventDefault(); const email = document.getElementById('loginEmail').value; const password = document.getElementById('loginPassword').value; fetch('http://127.0.0.1:8000/api/login/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email, password }), }) .then(response => response.json()) .then(data => { if (data.token) { document.getElementById('loginMessage').textContent = "로그인 성공! 토큰: " + data.token; } else { document.getElementById('loginMessage').textContent = "오류: " + data.error; } }) .catch(error => console.error('Error:', error)); }); </script>
백엔드에서 JSON 데이터 처리: DRF가 API Endpoint를 통해 JSON 데이터를 주고받습니다.
프론트엔드에서 fetch로 요청 보내기: fetch를 사용해 JSON 형식으로 데이터를 주고받습니다.
토큰 저장: 로그인 성공 시 받은 토큰은 localStorage 또는 sessionStorage에 저장해 인증에 사용합니다.const email = document.getElementById('email').value;
getElementById('email'): getElementById는 HTML 문서에서 특정 id를 가진 요소를 찾아 반환하는 메서드입니다. 여기서는 id="email"을 가진 요소를 찾습니다. 이 요소는 보통 <input type="email" id="email"> 형태일 것입니다.
.value: HTML 요소의 값을 가져오는 속성입니다. 이 경우, 사용자가 이메일 입력 필드에 입력한 값을 가져옵니다. 그 값을 email 변수에 저장합니다.fetch('http://127.0.0.1:8000/api/register/', { ... })
fetch: fetch는 자바스크립트에서 서버에 HTTP 요청을 보내는 함수입니다. fetch는 기본적으로 비동기적으로 작동하므로, 서버로부터의 응답을 기다리는 동안 페이지가 멈추지 않고 다른 작업을 계속할 수 있습니다.
'http://127.0.0.1:8000/api/register/': fetch 요청을 보낼 URL입니다. 여기서는 로컬 서버의 주소(127.0.0.1)에서 실행되는 register API 엔드포인트로 요청을 보내고 있습니다.
{ ... }: 이 부분은 fetch 요청의 옵션을 설정하는 객체입니다. 요청에 필요한 메서드, 헤더, 본문 등을 정의합니다.method: 'POST'
method: HTTP 요청 방식입니다. 여기서는 데이터를 서버로 보내는 방식인 POST를 사용합니다. POST 요청은 데이터를 서버로 제출할 때 사용됩니다.
headers: { 'Content-Type': 'application/json' }
headers: HTTP 요청에 포함될 추가 정보를 설정하는 객체입니다. 이 객체에서 설정한 내용은 서버에 대한 메타데이터로 포함됩니다.
'Content-Type': 'application/json': 요청 본문이 JSON 형식임을 서버에 알리는 헤더입니다. 이는 서버가 요청 본문을 JSON으로 파싱하도록 지시합니다.
body: JSON.stringify({ email, username, password, password2 })
body: HTTP 요청에 포함될 실제 데이터를 설정하는 부분입니다. 이 데이터는 서버에 전달되며, 주로 사용자로부터 받은 입력 값들입니다.
JSON.stringify(...): 자바스크립트 객체를 JSON 문자열로 변환하는 메서드입니다. 서버에 전송할 때는 문자열 형식으로 데이터를 보내야 하므로, 여기서는 email, username, password, password2 값을 객체로 묶고 이를 JSON 문자열로 변환합니다..then(response => response.json())
then: fetch 요청이 완료된 후 실행될 함수를 정의합니다. then은 Promise가 성공적으로 해결되었을 때 실행됩니다.
response: 서버의 응답을 나타냅니다. fetch 메서드는 서버에서 응답이 오면 이 response 객체를 반환합니다.
response.json(): 응답을 JSON 형식으로 변환하는 메서드입니다. 서버에서 받은 응답을 JSON으로 파싱하여, 그 결과를 data 변수로 전달합니다..then(data => { ... })
이 부분은 JSON으로 파싱된 응답 데이터를 처리하는 함수입니다.
data: 서버에서 받은 데이터를 담고 있는 변수입니다. 이 데이터는 JSON.stringify()로 전송한 값에 대해 서버가 반환한 응답입니다. 예를 들어, 회원가입 성공 여부나 에러 메시지가 포함될 수 있습니다.
if (data.message): data 객체에서 message라는 속성이 있는지 확인합니다. 이 속성은 서버에서 성공적인 응답이 오면 포함될 수 있습니다.
document.getElementById('registerMessage').textContent = "회원가입 성공!";: data.message가 있을 경우, 화면에 "회원가입 성공!"이라는 메시지를 표시합니다.
else: data.message가 없으면, data를 문자열로 변환하여 에러 메시지를 표시합니다..catch(error => console.error('Error:', error))
catch: fetch 요청이 실패할 경우 실행되는 함수입니다. 네트워크 오류나 서버 오류 등으로 인해 요청이 실패하면, 그 오류를 처리합니다.
error: catch 블록에서 발생한 오류를 나타냅니다.
console.error('Error:', error): 오류 내용을 콘솔에 출력하는 메서드입니다. 이 부분은 오류 발생 시 브라우저 개발자 도구의 콘솔에 에러 메시지를 표시합니다.
사용자가 email, username, password, password2 값을 폼에 입력합니다.
fetch로 POST 요청을 보내고, JSON 형식으로 서버에 데이터를 전달합니다.
서버에서 응답을 받으면, 그 응답이 JSON 형식으로 변환되고, data라는 변수에 저장됩니다.
응답에서 data.message가 있다면 성공 메시지를 화면에 출력하고, 없다면 오류 메시지를 표시합니다.
오류가 발생하면 catch에서 그 오류를 콘솔에 출력합니다.
이벤트란 사용자가 웹 페이지와 상호작용할 때 발생하는 특정 동작을 의미합니다. 예를 들어, 사용자가 버튼을 클릭하거나, 마우스를 올리거나, 키보드에서 키를 입력하는 등의 동작이 모두 이벤트에 해당합니다. 자바스크립트에서는 이러한 이벤트를 다룰 수 있도록 다양한 방법을 제공합니다.
이벤트의 예
클릭 이벤트: 사용자가 버튼, 링크, 이미지 등 요소를 클릭했을 때 발생합니다.
입력 이벤트: 사용자가 텍스트 필드에 입력할 때 발생합니다.
마우스 이벤트: 마우스를 요소 위로 올리거나, 클릭하거나, 드래그할 때 발생합니다.
폼 제출 이벤트: 폼을 제출했을 때 발생합니다.
event 객체
이벤트는 이벤트 객체를 포함하고 있습니다. 이벤트 객체는 발생한 이벤트에 대한 정보를 담고 있으며, 이벤트 핸들러에서 이를 사용할 수 있습니다. 예를 들어, 클릭 이벤트에서는 클릭한 요소, 클릭 위치 등의 정보를 담고 있는 객체가 event로 전달됩니다.
<button id="myButton">클릭하세요</button> <script> // 버튼 클릭 시 이벤트 발생 document.getElementById('myButton').addEventListener('click', function(event) { alert('버튼을 클릭했습니다!'); console.log(event); // 이벤트 객체 출력 }); </script>
addEventListener: 이 메서드는 특정 이벤트가 발생했을 때 호출될 함수를 등록하는 역할을 합니다. 여기서 'click' 이벤트가 발생하면, 두 번째 인수로 제공된 함수가 호출됩니다.
event: event는 이벤트 핸들러에 자동으로 전달되는 객체로, 발생한 이벤트에 대한 정보가 포함되어 있습니다. 예를 들어, 클릭 이벤트에서 event.target은 클릭된 요소를 가리킵니다.
event.preventDefault()
event.preventDefault()는 기본 이벤트 동작을 취소하는 메서드입니다. 예를 들어, 폼 제출 버튼을 클릭했을 때 페이지가 새로고침되는 기본 동작을 막을 수 있습니다.document.getElementById('myForm').addEventListener('submit', function(event) { event.preventDefault(); // 기본 제출 동작을 방지 alert('폼이 제출되었습니다.'); });
이 예제에서는 폼을 제출할 때 페이지가 새로 고침되는 기본 동작을 방지하고, 대신 alert() 메시지를 표시합니다.
alert('폼이 제출되었습니다.');는 자바스크립트의 alert() 함수입니다. 이 함수는 브라우저에서 알림 창을 띄워서 사용자에게 정보를 전달하는 기능을 합니다.
alert() 함수의 특징
간단한 알림 메시지: alert()는 사용자에게 간단한 메시지를 보여줄 때 사용됩니다. 예를 들어, 오류 메시지, 확인 메시지 등을 표시할 때 유용합니다.
동기적 실행: alert()는 사용자가 팝업 창을 닫기 전까지 JavaScript 코드의 실행을 멈추게 만듭니다. 즉, 팝업 창을 닫지 않으면 이후 코드가 실행되지 않습니다.
배웠다면 바로 써보자
<div class="hero-section">
<div class="text-center">
<h1 class="hero-title">맛봇에 오신것을 환영합니다.</h1>
<p class="hero-text">AI를 활용한 이 서비스를 언제 어디서나 찾을 수 있는 레시피를 통해 건강하고 자신만의 식단을 관리하세요.</p>
<button class="btn btn-learn-more" id="loginButton">로그인</button>
</div>
</div>
여기서 class 다음 id를 넣으면 된다. 그리고 뭐다?
<script>
document.getElementById('loginButton').addEventListener('click', function() {
window.location.href = 'accounts/login';
});
</script>
1. window
window는 브라우저의 전역 객체입니다. 즉, 웹 페이지를 실행하는 브라우저 환경 자체를 의미합니다.
window 객체는 브라우저의 여러 속성 및 메서드를 포함하고 있어, 웹 페이지에서 사용할 수 있는 중요한 기능들을 제공합니다. 예를 들어, alert(), console.log(), setTimeout() 등의 함수들이 window 객체에 포함되어 있습니다.
2. location
location은 window 객체의 속성 중 하나로, 현재 웹 페이지의 URL 정보를 포함합니다.
location을 사용하여 페이지의 URL을 가져오거나, 새 URL로 리디렉션하거나, URL의 특정 부분을 수정할 수 있습니다.
3. href
href는 location 객체의 속성으로, 현재 페이지의 전체 URL을 나타냅니다.
이 값을 변경하면, 페이지가 해당 URL로 리디렉션됩니다. 즉, 브라우저가 해당 주소로 이동합니다.
window.location.href는 현재 페이지의 URL을 가져오는 속성입니다.
'/login'을 이 속성에 할당하면, 브라우저는 현재 페이지에서 /login 경로로 리디렉션됩니다. 즉, /login 페이지로 이동하게 됩니다.
만약 /login에 네임스페이스가 있다면?
Django에서 URL에 네임스페이스를 사용하면, URL 패턴을 지정할 때 네임스페이스를 고려해야 합니다. 예를 들어, URL 패턴에 네임스페이스가 지정된 경우, urls.py에서 다음과 같이 정의할 수 있습니다:
# urls.py from django.urls import path from . import views app_name = 'auth' # 네임스페이스 'auth' 정의 urlpatterns = [ path('login/', views.login_view, name='login'), ]
이 경우, window.location.href = '/login'; 대신 네임스페이스를 고려한 URL 패턴을 사용해야 합니다.
네임스페이스를 고려한 리디렉션
Django에서 정의된 네임스페이스가 있는 경우, JavaScript에서 URL을 작성할 때는 해당 네임스페이스를 포함한 URL을 작성해야 합니다. 예를 들어, auth 네임스페이스가 있는 경우에는 다음과 같이 사용합니다:window.location.href = '/auth/login'; // 네임스페이스(auth)를 포함한 URL
event란 무엇인가?
event는 브라우저에서 발생한 특정 이벤트 객체(Event Object)를 나타냅니다.
버튼 클릭, 키 입력, 마우스 이동, 폼 제출 등의 이벤트가 발생하면 브라우저는 자동으로 이벤트 객체를 생성하고, 이를 이벤트 핸들러로 전달합니다.
쉽게 말하면
이벤트란 사용자가 웹 페이지와 상호작용할 때 발생하는 특정 동작을 의미합니다. 예를 들어, 사용자가 버튼을 클릭하거나, 마우스를 올리거나, 키보드에서 키를 입력하는 등의 동작이 모두 이벤트에 해당합니다. 자바스크립트에서는 이러한 이벤트를 다룰 수 있도록 다양한 방법을 제공합니다.
event가 있을 때
이벤트 핸들러 함수에서 event 객체를 명시적으로 선언하면, 해당 이벤트에 대한 정보를 사용할 수 있습니다.
document.getElementById('myButton').addEventListener('click', function(event) { console.log(event); // 클릭 이벤트 객체 출력 console.log(event.type); // 이벤트 타입 ('click') console.log(event.target); // 이벤트가 발생한 요소 (버튼) });
event 객체가 제공하는 정보:
type: 이벤트의 종류 (예: 'click', 'submit', 'keydown' 등)
target: 이벤트가 발생한 요소 (예: 클릭된 버튼)
currentTarget: 이벤트가 현재 처리되고 있는 요소
기타: 마우스 좌표, 키보드 입력 등
document.getElementById('myForm').addEventListener('submit', function(event) { event.preventDefault(); // 폼 제출 동작 중지 console.log('폼이 제출되었습니다!'); });
여기서 event.preventDefault()는 기본 동작(폼 제출)을 중단하기 위해 사용됩니다.
event가 없다면 기본 동작을 중지할 방법이 없습니다.
event가 없을 때
이벤트 핸들러에서 event를 명시적으로 선언하지 않으면, 이벤트 객체에 접근할 수 없습니다.
이 경우, 함수는 단순히 호출되고 추가적인 이벤트 정보를 사용하지 않습니다.
예시:document.getElementById('myButton').addEventListener('click', function() { console.log('버튼이 클릭되었습니다!'); // 단순 로그 출력 });
여기서는 event 객체를 사용하지 않기 때문에, 이벤트에 대한 추가 정보(예: target, type)를 얻을 수 없습니다.
언제 event를 사용하는가?
필요한 경우:
기본 동작을 막아야 할 때 (예: preventDefault(), stopPropagation())
이벤트 발생 정보를 참조할 때 (예: event.target)
마우스 좌표, 키 입력 등 이벤트 관련 데이터를 사용할 때
필요하지 않은 경우:
단순히 이벤트가 발생했는지 확인만 하면 될 때 (예: 버튼 클릭 시 메시지 출력)