์ ์ฅ์ ๊ท์น๋ช
์
WEB(FE) : Project/frontend
WEB(BE) : Project/backend
์ผ๊ธฐ๋ฅผ ์ฐ๋ฉด์ ๋ด๊ฐ ์๋ ๋ค๋ฅธ ์ฌ๋๋ค์ ์ด๋ค ๊ธ์ ์ ์๊น. ๋์ ์ด๋ค ๋ค๋ฅธ ์๊ฐ์ ๊ฐ์ง๊ณ ์์๊น ๊ณ ๋ฏผํ ๊ฒฝํ์ด ๋ง๋ค. ์ค๋งํธํฐ์ด ๋์คํ๋๋ฉด์ ๋ง์ ์ฌ๋๋ค์ด ์ดํ์ ์ผ๊ธฐ๋ฅผ ์ ๊ณ ์๊ณ , ์ด๋ฐ ๊ณ ๋ฏผ์ ๋ฐ์ํด ๊ณต์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์๋น์ค๋ ์ด๋ฏธ ์กด์ฌํ๋ค. ํ์ง๋ง ๊ณต์ ๊ธฐ๋ฅ์ ์๋๋๋ก ์ด๋ฆฌ๊ณ ์ ์ ๊ฐ์ ์ปค๋ฎค๋์ผ์ด์
์ ํ๋ฐํ๊ฒ ์ด๋์ด๋ด๋๋ฐ ์ฑ๊ณตํ ์๋น์ค๋ ์์ง๊น์ง ๋ณด์ด์ง ์๋๋ค. ๋น๋ฐ์ค๋ฌ์ด ๊ธ์ด๋ผ๋ ์ผ๊ธฐ์ ํน์ฑ ๋๋ฌธ์ผ์ง๋ ๋ชจ๋ฅด์ง๋ง ๋๋ ์์ง๊น์ง '๊ณต์ '์ ์ด์ ์ ๋ ์๋น์ค๊ฐ ์์๊ธฐ ๋๋ฌธ์ด๋ผ๊ณ ํ์
ํ๋ค. SNS๊ฐ ์ด๋ป๊ฒ ์ ์ ๊ฐ์ ๊ฒฐ์์ ๋ง๋ค๊ณ ๊ฐํ ํ์ฐ๋ ฅ์ ๋ง๋ค์ด๋ด๋์ง, ๊ทธ๋ผ์๋ ๋ถ๊ตฌํ๊ณ SNS๊ฐ ์ผ๊ธฐ์ฅ์ ๋์ฒดํ ์ ์๋ ์ด์ ๊ฐ ๋ฌด์์ธ์ง ๋ง์ด๋ค.
๊ณต์ ๋ผ๋ ์ปจ์
์ ํต์ฌ์ด ๋ SNS์ ํ์ฐ๋ ฅ๊ณผ ์ ์ ๊ฐ ๊ฒฐ์์ ์ผ๊ธฐ์ฅ์ ๋ฐ์ํ๊ณ SNS๊ฐ ๊ฐ์ง์ง ๋ชปํ ์์นด์ด๋ธ์ ๊ธฐ๋ฅ์ ๊ฐํํ์ฌ ์ง๊ธ๊น์ง์ ๋จ์ํ ์ผ๊ธฐ์ดํ์ด ์๋ ์ฌ๋๋ค์ด ์ผ์๊ณผ ์๊ฐ์ ๊ณต์ ํ๊ณ ๊ทธ๋ค์ ์ํ์ด ๋ ์ ์๋ '์ผ๊ธฐ ๊ณต์ ํ๋ซํผ'์ ๊ฐ๋ฐ์ ๋ชฉํ๋ก ํ๋ค.
Django RestFramework (DRF) ์ด์ฉ
Create React App (CRA) ๋น๋
Redux Tool Kit (RTK) ์ด์ฉ ์ํ๊ด๋ฆฌ
Raw Query ์์ด django ORM ์ด์ฉ
$ git clone "https://github.com/osamhack2022/WEB_SharedDiary_Nuri.git"
// ์๋ฒ ์คํ ์๋ด
$ cd Project
$ python3 -m venv venv
$ source venv/bin/activate
$ python3 -m pip install --upgrade pip
$ pip install -r requirements.txt
$ cd backend
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py runserver
// ํ๋ก ํธ์๋ ์คํ ์๋ด
$ cd Project/frontend
$ npm install
$ npm start
$ cd Aiden-Kwak
$ cat ๊ณฝ๋ณํ.json
{
"name" : "Kwak Byeong Hyeok (jeff721@cnsh.hs.kr)",
"Github" : "@Aiden-Kwak",
"Role" : ["FullStack", "UI/UX", "Product design"]
}
$ cd mingi123
$ cat ์ ๋ฏผ๊ธฐ.json
{
"name" : "Jung MinGi (alsrl123488@gmail.com)",
"Github" : "@mingi123",
"Role" : ["UI/UX", "Video Editing"]
}
ํ์๊ฐ์ , ๋ก๊ทธ์ธ
์์ด๋, ์ด๋ฉ์ผ, ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅ๋ฐ์ ํ์๊ฐ์ ์ด ์ด๋ฃจ์ด์ง๋๋ค.
๋ก๊ทธ์ธ์์ ์ด๋ฉ์ผ, ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅ๋ฐ์ต๋๋ค.
๋ก๊ทธ์์์ ํ์ง ์๋๋ค๋ฉด ๋ธ๋ผ์ฐ์ ์ข ๋ฃ ํ ์ฌ์ ์ํด๋ ๋ก๊ทธ์ธ์ด ์ ์ง๋ฉ๋๋ค.
์ผ๊ธฐ์ฅ ์์ฑ
๋ก๊ทธ์ธํ ์ ์ ๋ '๋์ ๊ณต๊ฐ'์์ ์ผ๊ธฐ์ฅ์ ์์ฑํ ์ ์์ต๋๋ค.
์ผ๊ธฐ์ฅ ์์ฑ์์ ์ผ๊ธฐ์ฅ ์ ๋ชฉ, ์ผ๊ธฐ์ฅ ์ค๋ช , ๋ํ ์ด๋ฏธ์ง๋ฅผ ์ ๋ ฅ๋ฐ์ต๋๋ค.
์์ฑ๋ ์ผ๊ธฐ์ฅ์ ์ ๋ ฅํ ๋ด์ฉ์ ๋ฐํ์ผ๋ก ์นด๋ํํ๋ก ๋ ๋๋ง๋ฉ๋๋ค.
์ผ๊ธฐ ์์ฑ
์ ์ ๋ ์์ ์ด ์์ฑํ ์ผ๊ธฐ์ฅ์์ ์ผ๊ธฐ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
์ผ๊ธฐ์ฅ์ ์ ๋ชฉ๊ณผ ๋ด์ฉ์ ์ ๋ ฅ๋ฐ์ต๋๋ค. ๋ด์ฉ ์ ๋ ฅ์ฐฝ์ WYSIWIG ์๋ํฐ๋ก, ์์ฑํ ์์์ด ๊ทธ๋๋ก ๋ ๋๋ง๋ฉ๋๋ค.
ํ๋ก์
์ ์ ํ๋ก์ ๊ธฐ๋ฅ์ ๊ตฌํํ์์ต๋๋ค.
ํ๋ก์, ํ๋ก์ ์ ์ ๊ฐ ๋ฆฌ์คํ ๋ฉ๋๋ค.
๋์ ๊ณต๊ฐ
์ผ์ข ์ ํ๋กํ๊ณต๊ฐ์ผ๋ก ์ ์ ๋๋ค์, ์ ์ ์์ด๋, ํ๋กํ์ด๋ฏธ์ง, ๋ฐฐ๊ฒฝ์ด๋ฏธ์ง๊ฐ ๋ํ๋๋ฉฐ,
ํ๋ก์ํ ์ ์ ์ ์ ์ ์ ํ๋ก์์๊ฐ ๋ํ๋๋ ๊ณต๊ฐ, ์์ฑํ ์ผ๊ธฐ์ฅ์ด ๋ชจ์ฌ์๋ ๊ณต๊ฐ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค. ์ฐ์ธก์ ์ถ์ฒ์ฌ์ฉ์๊ฐ ๋ฆฌ์คํ ๋ฉ๋๋ค. ํ๋กํ์นด๋๋ฅผ ํด๋ฆญํด ํด๋น ์ ์ ์ ๋์๊ณต๊ฐ์ผ๋ก ์ด๋ํ ์ ์์ต๋๋ค.
ํ์๋ผ์ธ
๊ฐ์ ๋ ๋ชจ๋ ์ ์ ๋ค์ ์ผ๊ธฐ๊ฐ ์ต์ ์์ผ๋ก ์ ๋ฐ์ดํธ๋์ด ๋ฆฌ์คํ ๋ฉ๋๋ค.
์ถ์ฒ์ผ๊ธฐ์ฅ ๊ณต๊ฐ์ ๋ค๋ฅธ ์ฌ์ฉ์๋ค์ ์ผ๊ธฐ์ฅ์ด ๋ฆฌ์คํ ๋ฉ๋๋ค.
1. ๊ณ์ ๊ด๋ จ (์ ์ , ํ๋กํ)
Table User {
id int [PK]
username char(20) [unique, not null]
email varchar [unique, not null]
created_at datetime
updated_at datetime
}
Table Profile {
user int [PK]
nickname char(20) [unique, not null]
self_intro char(250)
profile_image image
background_image image
following int
follower int
created_at datetime
updated_at datetime
}
Ref: Profile.user - User.id
Note: 'One-to-One relation'
Ref: Profile.following > Profile.user
Note: 'Many-to-Many relation'
Ref: Profile.follower > Profile.user
Note: 'Many-to-Many relation'
2. ์ผ๊ธฐ๊ด๋ จ(์ผ๊ธฐ์ฅ, ์ผ๊ธฐ)
Table Diary {
id int [PK]
writer int
title char(45) [not null]
content varchar [not null]
note int
created_at datetime
updated_at datetime
to_open boolean
} Note: '์ผ๊ธฐ๋ชจ๋ธ'
Table Note {
id int [PK]
writer int
title char(45)
description char(150)
image image
diary int
created_at datetime
updated_at datetime
to_open boolean
} Note: '์ผ๊ธฐ์ฅ ๋ชจ๋ธ'
Ref: Diary.writer - User.id
Note: 'One-to-One relation'
Ref: Diary.note - Note.id
Note: 'One-to-One relation'
Ref: Note.diary > Diary.id
Note: 'One-to-One relation'
jwt(Json Web Token) ๋ฐฉ์์ ์ธ์ฆ์ ๊ตฌํํ์๋ค. ์๋๋ ๊ตฌํ๊ณผ์ ์ ๋ํ ๊ฐ๋ตํ ์ค๋ช ์ด๋ค.
1. ํ ํฐ ์์ฑ
@property def token(self): return self._generated_jwt_token()
def _generated_jwt_token(self): dt = datetime.now()+timedelta(days=60)
token = jwt.encode({
'id':self.pk,
'exp':dt.utcfromtimestamp(dt.timestamp())
}, settings.SECRET_KEY, algorithm='HS256')
return token
> accountapp/models.py์ ์ผ๋ถ์ด๋ค. token์ ์ ์ id, 60์ผ๋ก ์ค์ ํ ๋ง๋ฃ์๊ฐ(dt), ์ ์ฉ์๊ณ ๋ฆฌ์ฆ(HS256)์ ์ธ์ฝ๋ฉํ ๊ฐ์ ํฉ์น๋ค SECRET_KEY๋ก hashing๋์ด ๋ง๋ค์ด์ง๋ค.
```python
1. ์ ์ ์์ฑ
class RegistrationAPIView(APIView):
permission_classes = (AllowAny,)
serializer_class = RegistrationSerializer
renderer_classes = (UserJSONRenderer,)
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
accountapp/views.py ์ ์ผ๋ถ์ด๋ค. post ์์ฒญ์ ๋ฐ๊ฒ ๋๋ฉด RegistrationSerializer์ ์์ฒญ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋๊ธฐ๊ณ ์ ํจ์ฑ๊ฒ์ฌ๋ฅผ ํต๊ณผํ๋ฉด ์ ์ ๋ฅผ ์์ฑํ๋ค. RegistrationSerializer๋ accountapp/serializers.py ์ฐธ๊ณ .
๋ก๊ทธ์ธ๊ธฐ๋ฅ, ๊ทธ๋ฆฌ๊ณ ๋ก๊ทธ์ธ ์ดํ ํ์ด์ง๊ฐ ์๋ก๊ณ ์นจ๋๊ฑฐ๋ ๋ฆฌ๋ค์ด๋ ํธ๋์ด๋ ๋ก๊ทธ์ธ์ํ๋ฅผ ์ ์งํ ์ ์๋๋ก ๊ตฌํํ์๋ค.
1. ๋ก๊ทธ์ธ
class LoginSerializer(serializers.Serializer): email = serializers.EmailField() username = serializers.CharField(max_length=20, read_only=True) password = serializers.CharField(max_length=128, write_only=True) last_login = serializers.CharField(max_length=255, read_only=True) token = serializers.CharField(max_length=255, read_only=True) id = serializers.ReadOnlyField()
def validate(self, data):
email = data.get('email', None)
password = data.get('password', None)
if email is None:
raise serializers.ValidationError(
'email address is required to Login'
)
if password is None:
raise serializers.ValidationError(
'password is required to Login'
)
user = authenticate(username=email, password=password)
if user is None:
raise serializers.ValidationError(
'user with this email and password was not found'
)
if not user.is_active:
raise serializers.ValidationError(
'This user has been deactivated'
)
user.last_login = timezone.now()
user.save(update_fields=['last_login'])
return {
'id': user.pk,
'email':user.email,
'username': user.username,
'last_login': user.last_login,
'token': user.token,
}
> ๋ค์์ accountapp/serializers.py์ ์ผ๋ถ์ด๋ค. ๋ก๊ทธ์ธ๊ธฐ๋ฅ์ ํต์ฌ์ ์ ์ ๊ฐ ์ฌ๋ฐ๋ฅธ ์ธ์ฆ์ ๋ณด๋ฅผ ์
๋ ฅํ๋์ง์ ํ์ธ, ๊ทธ๋ฆฌ๊ณ ์ธ์ฆ๊ณผ์ ์ ์ํ ํ ํฐ ๋ฐ ์ ์ ์ ๋ณด ๋ฐํ์ด๋ค.<br>
> django์ authenticate ํจ์๋ฅผ ์ด์ฉํด usernameํ๋๋ก ์ค์ ํ email, ์
๋ ฅ๋ฐ์ password์ ์กฐํฉ์ DB์ ๋งค์นญํด ํ์ธํ๋ค.
> accountapp/views.py์ LoginAPIView์์๋ ์ด serializer์ ์์ฒญ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ด๊ณ ์ ํจ์ฑ๊ฒ์ฌ๋ฅผ ๊ฑฐ์น๋ค ๋ฆฌํดํ๋ค.
<br>
```python
2. ์ํ์ ์ง ๊ตฌํ
const onSubmit = async() => {
const url = "/accounts/login";
const userdata = {
'email': email,
'password': password
};
const config = {
"Content-Type": 'application/json'
};
await axios
.post(url, userdata, config)
.then(function (res) {
if (res.data.user.token) {
localStorage.setItem('userdata', JSON.stringify(res.data));
localStorage.setItem('token', res.data.user.token);
}
})
.catch(function (error) {
console.log(error);
setLoginError(true);
});
};
์ ์ฝ๋๋ frontend/src/account/Login.jsx์ ์ผ๋ถ์ด๋ค.
๋ก๊ทธ์ธ ๋ฒํผ ํด๋ฆญ์ ์๋ํ๋ ํจ์๋ก ๋ก๊ทธ์ธ ์ ๋ ฅ์ ๋ณด๋ฅผ ๋ด์ ์๋ฒ์ post ์์ฒญ์ ๋ณด๋ธ๋ค. ๋ก๊ทธ์ธ์ ์ ์งํ๋ค๋ ๊ฒ์ ๊ณ์ํด ๋ก๊ทธ์ธํ ์ ์ ์ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ ๊ฒ์ด๋ค. ์ด๋ฅผ ์ํด axios ์์ฒญ์ด ์ฑ๊ณตํ์๋ ๋ฐํ๋ฐ์ ์์ฒญ์ ๋ธ๋ผ์ฐ์ ์ localStorage์ ๋ด์๋ค. ํด๋ผ์ด์ธํธ์์๋ ๋ก๊ทธ์ธ ์ดํ ์ ์ฅ๋ ํด๋น ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ๋์ํ๊ฒ ๋ ๊ฒ์ด๋ค.
1. ์ธ์ฆ๊ตฌํ
class JWTAuthentication(authentication.BaseAuthentication):
authentication_header_prefix = 'Token'
def authenticate(self, request):
auth_header = authentication.get_authorization_header(request).split()
auth_header_prefix = self.authentication_header_prefix.lower()
if not auth_header:
return None
if len(auth_header) == 1:
return None
elif len(auth_header) > 2:
return None
prefix = auth_header[0].decode('utf-8')
token = auth_header[1].decode('utf-8')
if prefix.lower() != auth_header_prefix:
return None
return self._authenticate_credentials(request, token)
def _authenticate_credentials(self, request, token):
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
except:
msg = 'Invalid authentication. Could not decode token.'
raise exceptions.AuthenticationFailed(msg)
try:
user = User.objects.get(pk=payload['id'])
except User.DoesNotExist:
msg = 'No user matching this token was found.'
raise exceptions.AuthenticationFailed(msg)
if not user.is_active:
msg = 'This user has been deactivated.'
raise exceptions.AuthenticationFailed(msg)
return (user, token)
๋ค์์ backend/accountapp/backends.py์ ์ผ๋ถ์ด๋ค.
์ผ๊ธฐ์ ๋ด์ฉ์ ๋ก๊ทธ์ธ์ ํ์ง ์์ ์ ์ ๋ ์ ๊ทผํ ์ ์๋ค. ํ์ง๋ง ์ผ๊ธฐ์์ฑํ์ด์ง์ ๋ก๊ทธ์ธ์ ํ์ง ์์ ์ ์ ๊ฐ ์ ๊ทผํ ์ ์์ด์๋ ์๋๋ค. Django๋ DRF๋ ๊ธฐ๋ณธ์ ์ผ๋ก JWT ์ธ์ฆ์ ์ง์ํ์ง ์๋๋ค. ์์ ์ฝ๋๋ ์ด๋ฅผ ์ํ JWT ์ธ์ฆ ์ฝ๋์ด๋ค.
authenticate ํจ์๋ ์ธ์ฆ ํ์์ฌ๋ถ์ ๊ด๊ณ์์ด ๋ชจ๋ ์์ฒญ์์ ํธ์ถ์ด ๋๋ค. ์ธ์ฆ์คํจ์ None์, ์ฑ๊ณต์ (user, token) ์กฐํฉ์ ๋ฐํํ์ฌ ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ค. _authenticate_credentials ๋ฅผ ํตํด token์ decodeํ๊ณ payload์ ๋ด๊ธด id์ ์ ํจ์ฑ์ ํ์ธํด ์ต์ข ์ ์ผ๋ก ์ธ์ฆ์ ๋ง๋ฌด๋ฆฌํ๋ค.
1. ํ๋กํ ๋ชจ๋ธ(ํ๋ก์๊ณผ ํ๋ก์ ํ๋)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
username = models.CharField(max_length=20, unique=True, blank=False, null=True)
nickname = models.CharField(max_length=20, unique=True, blank=False)
slug = models.SlugField(null=True, unique=True)
self_intro = models.TextField(blank=True)
profile_image = models.ImageField(upload_to='profile/')
background_image = models.ImageField(upload_to='profile/', null=True, blank=True)
following = models.ManyToManyField('self', symmetrical=False, related_name='followings', blank=True, null=True)
follower = models.ManyToManyField('self', symmetrical=False, related_name='followers', blank=True, null=True)
ํ๋กํ ๋ชจ๋ธ์์ following๊ณผ follower ํ๋๋ฅผ Profile๋ชจ๋ธ์ ๋ํ์ฌ M:N ๊ด๊ณ๋ฅผ ์ค์ ํ์๋ค.
2. ํ๋ก์ฐ API
class FollowAPIView(APIView):
permission_classes = (IsAuthenticated,)
renderer_classes = (UserJSONRenderer,)
serializer_class = ProfileSerializer
def post(self, request, *args, **kwargs):
user = request.user
profile = Profile.objects.get(user=user)
another_user = User.objects.get(id=request.data['id'])
another_profile = Profile.objects.get(user=another_user)
profile.following.add(another_profile)
another_profile.follower.add(profile)
serializer = self.serializer_class(Profile.objects.get(user=user))
return Response(serializer.data, status=status.HTTP_200_OK)
๋ค์์ accountapp/views.py์ ์ผ๋ถ์ด๋ค. ํด๋ผ์ด์ธํธ์์ axios post์์ฒญ์ ํตํด ๋์์ user_id๋ฅผ ์์ฒญ๋ฐ์ ๊ฒ์ด๋ค.
์ด๋ฅผ ํตํด ๋ณธ์ธ(profile), ์๋(another_profile)์ ์ ์ธํ๊ณ ํ๋์ add()ํจ์๋ฅผ ์ด์ฉํด ๋ณธ์ธ์ followingํ๋์ another_profile์, ์๋์ followerํ๋์ profile์ add() ํ๋ค.
1. ์ผ๊ธฐ์ฅ ๋ชจ๋ธ
class Note(TimestampedModel):
writer = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=45, null=True)
description = models.TextField(max_length=150, blank=False, null=True)
image = models.ImageField(upload_to='note/', null=True, blank=True)
diary = models.ManyToManyField('accountapp.Diary', related_name='note', blank=True, null=True)
to_open = models.BooleanField(default=True, choices=OPEN_CHOICES) #๋ฏธ๊ตฌํ
@property
def writer_pk(self):
return self.writer.id
์ผ๊ธฐ์ฅ ๋ชจ๋ธ์ ์์ฑ์, ์ ๋ชฉ, ์ค๋ช , ์ด๋ฏธ์ง, ์ผ๊ธฐ๋ก ๊ตฌ์ฑ๋๋ค.writer๋ User์ ์ธ๋ํค๋ก, diary๋ Note์์ ๋ค๋๋ค๊ด๊ณ๋ก ํ์ฑ๋๋ค.
1. ์ผ๊ธฐ ์์ฑ API
class DiaryCreateView(APIView):
permission_classes = (IsAuthenticated,)
serializer_class = DiarySerializer
def post(self, request, *args, **kwargs):
serializer = DiarySerializer(data=request.data)
note_id = request.data['id']
note = Note.objects.get(id=note_id)
user=request.user
if serializer.is_valid():
post = Diary.objects.create(
writer=user,
title=request.data['title'],
content=request.data['content'],
note=note,
image=request.data['image'],
to_open=request.data['to_open']
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
์ผ๊ธฐ์ฅ์์์ ์ผ๊ธฐ๋ฅผ ์์ฑํ ๋ ์ฌ์ฉ๋๋ API๋ค. ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ์์ฒญ๋ฐ์ ์ผ๊ธฐ์ฅ์ id๋ก Note object๋ฅผ ์ฐพ๊ณ serializer์ ์ ํจ์ฑ๊ฐ์ฌ๋ฅผ ๊ฑฐ์น๋ค Diary object๋ฅผ ์์ฑํ๋ค.
์ด๋ก์จ ์ฌ์ฉ์๋ ์ข ๋ฅ๋ณ๋ก ์ผ๊ธฐ์ฅ์ ์์ฑํ๊ณ ์ผ๊ธฐ์ฅ ์์ ๊ทธ์ ๋ง๋ ์ผ๊ธฐ๋ค์ ์ ์ ์ ์๋ค.
1. ๋๋ค์, ์ ์ ๋ค์์ผ๋ก ํ๋กํ ๊ฒ์ํ๋ API
class ProfileSearchAPIView(APIView):
serializer_class = ProfileSerializer
def get(self, request, *args, **kwargs):
input_data = request.query_params['inputData']
profile = Profile.objects.filter(Q(nickname__icontains=input_data) and Q(username__icontains=input_data))
serializer = self.serializer_class(profile, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
ํด๋ผ์ด์ธํธ์์ Onchange event๋ก axios get์์ฒญ์ ๋ฐ์ params๋ก ์ ์ก๋ input ๋ฐ์ดํฐ๋ฅผ profile db์ ๋์กฐํ๋ค. nickname๊ณผ username์ ๋ํ์ฌ input์ ๋ฌธ์์ด์ด ํฌํจ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํดํ๋ค.