TIL - 05/28 레이어드 패턴

Sung Jun Jin·2020년 5월 28일
0

TIL

목록 보기
21/25
post-custom-banner

출처 : 깔끔한 파이썬 탄탄한 백엔드 (송은우 저)

레이어드 패턴

백엔드 API 코드에 가장 널리 적용되는 패턴 중 하나는 레이어드 아키텍처(Multi-tier) 패턴이다. 레이어드 아키텍처는 코드를 논리적인 부분 혹은 역할에 따라 독립된 모듈로 나누어서 구성하는 패턴이다. 각 모듈은 서로의 레이어에 의존성을 가지고 전체의 시스템을 구성한다.

이렇게 레이어별로 영역을 명확히 구분해놓으면, 코드의 확장성과 테스트의 효율성이 높아진다.

일반적으로 다음과 같은 3개의 레이어가 존재한다

  • Presentation Layer (View) : 사용자와 직접적으로 연결되는 부분이다. 웹사이트에서는 UI 부분에 해당, 백엔드에서는 엔드포인트 부분에 해당한다. 호출될 API의 엔드포인트를 정의하고 HTTP request들을 읽어 들이는 로직을 구현한다.
  • Buisness Layer (Service) : 로직을 구현하는 부분이다. 예를들어 사용자에 대한 권한 확인, 글자 수 제한등의 비즈니스 로직을 담당한다
  • Persistence Layer (Model) : 데이터베이스와 관련된 로직을 구현하는 부분이다. Buisness layer에서 필요한 데이터 생성, 수정, 읽기등을 처리한다.

단방향 의존성

레이어드 아키텍처의 핵심 요소는 단방향 의존성이다. 각각의 레이어는 자신의 레이어보다 하위에 있는 레이어에만 의존한다. 반대로 상위에 있는 레이어에서는 완전히 독립적이라고 할 수 있다.

Presentation Layer -> Buisness Layer -> Persistence Layer 순

Presentation Layer에서는 로직을 처리하기 위해 Buisness Layer가 필요하고, Buisness Layer에서는 데이터베이스에서 데이터를 읽어들이기 위해 Persistence Layer가 필요하다.

레이어드 아키텍처 적용하기

미니 트위터 api의 회원가입 과정에 레이어드 아키텍처를 적용해보자. 여기서는 pymysql 모듈을 사용했다. 우선 디렉터리 구조는 각 레이어별로 다음과 같이 구성해준다.

$ mkdir {view,service,model}

├── model
├── service
└── view

Persistence Layer (Model)

가장 하위 레이어인 Persistence Layer 부터 시작해보자. 데이터베이스에 직접 접근을 해야 하므로 DAO 클래스를 구성해준다. 참고로 DAO는 (Data Access Object)의 줄임말로써, DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 Object를 뜻한다. 회원(User)에 대한 기능을 담당하는 DAO 이므로 UserDao 클래스를 생성해준다.

class UserDao:
    def __init__(self, database):
        self.db = database 
            
    def insert_user(self, user):
        cursor = self.db.cursor()
        sql =  """
                insert into users(
                email,
                password
                ) values (
                    %s,
                    %s
                )
                """
        
        cursor.execute(sql,(user['email'],user['password']))
        self.db.commit()
        self.db.close()                          

Buisness Layer (Service)

로직을 담당하는 service 코드를 작성해보자. 회원(User)에 대한 로직을 담당하는 UserService 클래스를 생성해준다. 그 안에서 사용자 회원가입 로직을 처리하는 create_new_user 메소드를 구현한다.

import bcrypt

class UserService:

    def __init__(self, user_dao):
        self.user_dao = user_dao

    def create_new_user(self, new_user):
        new_user['password'] = bcrypt.hashpw(new_user['password'].encode('utf-8'),bcrypt.gensalt())
        new_user_id = self.user_dao.insert_user(new_user)
        return new_user_id

간단하게 사용자의 비밀번호를 bcrypt를 사용해 단방향 해쉬하는 로직만 추가했다. 그 이후에 앞서 생성했던 UserDao 클래스의 insert_user 메소드를 사용해준다. 인자는 상위 레이어인 Presentation Layer(view)에서 건네받은 회원가입할 사용자의 email과 password가 담긴 request 딕셔너리이다.

Presentation Layer (View)

마지막으로 엔드포인트를 정의해주는 view 레이어로 올라온다. @app.route 데코레이터를 통해 엔드포인트의 url을 설정해주고 http method를 정의해준다. 위에서 구현한 UserService 클래스의 create_new_user 메소드를 호출하고 인자로 http request를 json화 해서 넘겨준다.

from flask import jsonify, request

def create_endpoints(app, services):
    user_service = services.user_service

    @app.route('/sign-up', methods=['POST'])
    def sign_up():
        new_user = request.json
        new_user = user_service.create_new_user(new_user)

        return jsonify({'message' : 'ok'}), 200

app.py

3가지 레이어에 대한 코드 구현을 다 끝냈다고 해서 회원가입 엔드포인트가 호출되는것은 아니다. 각 레이어를 모두 연결해줘야 하는데 이 작업을 app.py에서 해주면 된다. app.py는 각 레이어의 클래스나 모듈들을 import해 연결시켜주고, Flask 애플리케이션을 생성하는 역할을 한다.

import pymysql
import pymysql.cursors

from flask      import Flask
from flask_cors import CORS


from config     import db
from model      import UserDao
from service    import UserService
from view       import create_endpoints

class Services:
    pass

def create_app(test_config = None):
    app = Flask(__name__)
    app.debug = True
    CORS(app)

    if test_config is None:
        app.config.from_pyfile('config.py')
    else:
        app.config.update(test_config)

    database = pymysql.connect(

        host        = db['host'],
        user        = db['user'],
        password    = db['password'],
        db          = db['database']
    )

    user_dao = UserDao(database)    
    services = Services
    services.user_service = UserService(user_dao, app.config)

    create_endpoints(app, services)
    return app
  • 위에서 생성한 모든 레이어의 클래스나 모듈들을 import 해준다.
  • create_app 메소드에서는 Flask 어플리케이션을 생성해 app 변수에 담아주고 debug 모드를 활성화시킨다. pymysql.connect 메소드를 사용해 db와 연결시켜준다. 데이터베이스 설정은 config.py에 딕셔너리로 저장해 두었다.
  • 가장 하단의 레이어부터 차례대로 클래스 인스턴스를 생성해준다. 생성해준 클래스 인스턴스를 상위 레이어에 넘겨줌으로써 각 레이어간의 의존관계를 세팅해준다
  • 마지막으로 view에서 import한 create_endpoint 메소드를 실행하여 엔드포인트를 생성해준다.
profile
주니어 개발쟈🤦‍♂️
post-custom-banner

0개의 댓글