ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [애플리케이션] React + Django + Nginx를 EC2에 배포! 거기에 Docker를 곁들인.. (1)
    일하며 늘어가는 지식 2024. 11. 17. 00:35

    시작

     

    이번에 작성할 글은 회사에서 이전에 배포했던 웹(Streamlit 기반)을 홀로 고도화를 진행하면서,

    배운 지식(?)들을 주로 설명하겠습니다.

     

    에러도 많이 있었는데, 딱히 기록을 하지 않았어서 기억나는 에러들을 위주로 쓰겠습니다...

     

    근데, 사실 고도화를 하면서 성장을 했다는 점은 인정한다.

    백엔드랑 프론트엔드 지식 전무했기에..


    문제

    우선 1차적 문제는 회사 규모가 작아 백엔드랑 프론트엔드를 하는 인원이 0명이다.

    (내가 하고있으니까 1명인가?)

     

    그래서 일단 Streamlit으로 뼈대가 구성된 웹 페이지를 다른 분이 만들어 놓았고,

    내가 그걸 이어 받아서 기능, UI를 추가 및 수정하는 방식으로 기간 내에 프로젝트를 끝내었었다.

     

    애초에 Streamlit 자체가 데이터분석을 위한 임시(?) 웹으로 주로 이용되는 툴로 주로 이용되고 있는 것으로 알고 있었는데,

    Streamlit으로 이렇게까지 할 수 있구나?를 알게 된 경험이긴 했다.

     

    2차적 문제는 웹 애플리케이션의 구성? 설계에 관한 문제였다.

     

    기존 웹을 접속하면 컴포넌트를 불러오는데 평균적으로 2초가량 걸리는 등 사용자가 이용하는데, 답답함을 느낄 정도의 로딩속도를 보여줬다. 

     

    이는, Streamlit으로 구성된 웹 자체가 클라이언트와 서버를 구분보다는 통합(?)되어 있기 때문이었다.

    (백엔드의 존재이유인가...)

     

    원하는 UI를 구현하는데, 큰 어려움이 존재했다. (프론트엔드의 존재이유인가...)

     

    여러 가지 이유가 있지만, 가장 큰 건 운영&관리를 하기에 코드가 너무 복잡하고, 데이터 플로우를 설명하기 애매했다는 점이다.

     

    그래서 겪었던(기억에 존재하는...) 문제를 정리하면 다음과 같다.

     

    1. Streamlit이 서버와 클라이언트가 딥하게 연결되어 있어서, 성능 최적화와 리소스 제어가 제한적이다.
    2. 복잡한 UI를 구현하는 게 어렵거나 불가능했다.
    3. 애플리케이션 내부에서 작동하는 상태 관리에 제한적이며, 플로우를 파악하는데 소모되는 리소스가 크다.
    4. 보안이 취약하며, 다른 기능을 추가하는 로직 구현이 복잡하거나 어렵다.

    문제 해결을 위한 고민

     

    어찌저찌 애플리케이션 아키텍처(?)를 구상하는 것은 쉬웠다.

    사실, 아는 게 없어서 간단하게 생각했다.

    이렇게..(심플하네!)

     

    Python 기반의 Django(Rest Framework)을 백엔드

    JavaScript 기반(??)의 프레임워크인 React를 프론트엔드

     

    그래서 사용자와의 상호작용을 React가 받아주고,
    Django랑 API로 처리하는 과정으로 구현하는 방법을 선택했다.

    -> 여기서 문제는 둘 다 학습하면서 진행했다는 점..

     

    React(클라이언트), Django(서버)로 구성하면서, 내 나름의 생각으로는... 1번, 3번, 4번이 한 번에 해결이 된다고 볼 수 있었다.

     

    간단하게 설명하면, 서버와 클라이언트를 각각 분리하였기 때문에 서버, 클라이언트 각각의 측에서 성능을 최대한 최적화가 가능할 것이라고 생각했기 때문이다. (1번)

     

    서버, 클라이언트로 분리하면서 데이터의 플로우를 직관적으로 이해하기가 좋아질 것 이라고 생각했고, 내부의 상태를 관리하는데 유연하게 이용이 가능하다고 생각했다.(3번)

     

    그리고  서버, 클라이언트를 분리하고 HTTPS, 장고 내부에서 RSA 암호화, 쿠키 적용등을 이용하면 데이터 보안에 더 효과적이지 않을까란 생각 했다.(4번)

     

    마지막으로, React를 이용해서 반응형 UI를 만드는 게 Streamlit 보다 쉬워 보였기 때문이다..

     


    학습 내용

     

    Django랑 React 학습 내용이랑 어떻게 고도화에 적용했는지를 쓰겠습니다.

     

    지금 고도화한 웹에서 Django는 API를 전담하고 있어서, API를 만드는데 필요한 방식(?)을 위주로 학습을 진행했고

    React는 각 컴포넌트를 만들어 하나의 웹으로 표현되도록 진행했다.

     

    Django의 경우 API를 만들기는 어렵지 않습니다.

     

     주로 API 만들기를 진행했기 때문에, 작성도 API 만들기를 위주로 진행하려고 합니다.

     

    API를 만들기 위해서 중요한 게, Django로 만들게 되면, View, Serializer, model, urls를 주로 사용하게 됩니다.

     

    여기서 Views는 우리가 만든 API가 어떤 형태로 작동될지를 주로 작성하게 됩니다.

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import status
    from django.conf import settings
    
    
    # 클래스 형태로 API 구성시 #
    class LoginView(APIView):
        # def get(self, request):
        	# 요청 형식에 따라 정의가능
            
        def post(self, request):
        	username = request.data.get("username")
            password = request.data.get("password")
            
            try:
                # AWS Cognito에 username, password를 이용해서 로그인을 하고 존재하면 로그인 성공! #
                # 로그인 성공 응답에 쿠키 설정해서 반환 #
                # 실제엔 OTP 까지 구현해둠 #
            
            except:
            	#응답 실패시 로그#

     

    코드를 보게 되면 주로 DRF라 불리는 프레임 워크를 이용해서 API를 구성했다.

    클래스 형태나 함수 형태 두 가지로 구성이 가능하며, 클래스의 경우 내부에 API 형태를 적어두고 정의를 해두면 된다.

     

    post 요청의 경우로 하게 되면 리액트 부분에서 유저이름과 패스워드를 API를 통해서 Django로 보내어 로그인 로직 및 검증을 통해 응답을 내고, 그 응답에 쿠키를 설정해서 반환하는 식으로 구성했다.

     

    이 부분은 기존에 회원리스트를 AWS Cognito를 이용해서 관리하고 있었기에 이어서 사용했다.

     

    예시 코드는 넣어두지 않았지만 여러 API 중 로그인 시에 Username입력 시

    그 Username이 Cognito에 존재하는지 찾고,

    OTP 코드가 등록되어 있으면 바로 비밀번호 밑에 라인에 OTP 확인 코드 넣거나, 등록되어 있지 않으면 OTP 등록하고 OTP 확인 코드 넣는 로직을 추가 구현했다.

     

     

    Serializer를 이용하게 되면 데이터의 형식(?)을 지정해 주는 방식으로 주로 작성하게 됩니다.

    from rest_framework import serializers
    
    
    class validationcheck(serializers.Serializer):
    	# 유효성을 체크하기 위해서 들어오는 데이터 타입 정의 #
        
        # 예시 #
        
        usernaem = serializers.CharField(max_length=40, required=True)
        password = serializers.CharField(write_only=True, required=True)
        # write_only는 비밀번호를 클라이언트에서만 받기위해 설정 #

     

    이런 식으로 고도화에서 주로 데이터의 유효성 검사를 위해서(?) 사용했습니다.

     

    실제로는 username과 password는 Cognito로 검증하기 때문에 필요 없지만, 예시를 위해 넣었습니다.

    저렇게 하고 아래에 최소 조건을 더 추가하여 검증을 복잡하게 할 수 있습니다.

     

     

    model의 경우엔 데이터베이스의 형태를 지정해 주는 방식으로 주로 작성하며, 이 고도화의 경우엔 이용하지 않았습니다.

    from django.db import models
    from django.contrib.auth.models import BaseUserManager
    
    class UserManager(BaseUserManager):
        # 유저를 만드는 모델 #
        def create_user(self, username, password=None, **extra_fields):
            user = self.model(username=username, **extra_fields)
            user.set_password(password)  # 비밀번호 해싱
            user.save(using=self._db)
            return user

     

    고도화에선 사용하지 않았지만, 이렇게 model에서 커스터마이징 해서 구현할 수 있습니다.

    간단하게 유저를 만드는 메서드를 만들었습니다.

     

    urls는 API의 URL 패턴을 지정해 주게 됩니다.

    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('register/', views.RegisterView.as_view(), name='register'),  # 회원가입
        path('login/', views.LoginView.as_view(), name='login'),  # 로그인
    ]

     

    이렇게 URL  패턴을 지정해 줄 수 있는데, 참고해야 할 게 있다.

     

    Django 프로젝트를 만들면 그 내부에서 주로 API 형태를 나누어서 앱을 또 만들게 된다.

     

    예를 들어 '쇼핑'이라는 프로젝트를 만들고, 내부에 '유저', '판매자' 이런 식의 앱을 만들고,

    '유저'의 앱 내부엔 유저에 관련된 API를 만드는 것이다.

     

    그럼 쇼핑이라는 프로젝트의 유저의 API를 이용하기 위해선 쇼핑의 URL과 유저의 URL을 설정해야 한다는 것이다.

    -> 한 줄로 쓰면, 프로젝트의 메인 URL과 각각의 앱 URL 설정하면 된다!

     

    # 쇼핑의 urls.py
    from django.urls import path, include
    
    urlpatterns = [
        path('users/', include('유저.urls')),  # 유저 urls 를 포함함
    ]
    
    
    # 유저의 urls.py
    from django.urls import path
    from . import views
    
    urlpatterns = [
        path('register/', views.RegisterView.as_view(), name='register'),  # 회원가입
        path('login/', views.LoginView.as_view(), name='login'),  # 로그인
    ]

     

    이런 식으로 하게 되면

    login의 url은 users/login/ 형태로 된다는 말씀

     


     

    어 적고 보니 생각보다 기록할 게 없다...

    뭔가 나름 많았던 거 같은데, 배포할 때가 가장 기억에 남아서 그런가..

     

    하여튼 나머지는 다음 편에 올려야겠다...

     

    여기까지 쓰면서 느낀 점은 기록을 안 하면 다 까먹는다!

    +

    그때그때 기록해야 하는데, 막상 기록 안 하게 된다는 슬픈 스토리...

     

    그리고 Django로 API 만들면서 느낀 거지만, 되게 FastAPI보다 생산성이 좋다고 느꼈다.

    뭐냐면, 학창 시절에 반에 착하고 공부 잘하는 친구한테 모르는 걸 물어본 느낌이랄까?

    이것저것 주는 느낌이었다!!

     

     


     

     

Designed by Tistory.