프로젝트, 연구/도서관 관리 사이트

[도서관 관리 사이트] 4. 동작 흐름, 파일 간 데이터 연결

CSE 2025. 10. 13. 12:37

이번 글은 추가적인 구현에 대한 얘기는 하지 않고 

프로젝트 전체의 흐름과 데이터의 전달에 대해 설명할 예정이다.

이전 글들과 설명이 중복 되는 내용도 있겠지만 자료형까지 조금 더 원리를 자세히 설명할 예정이다.

 

0. flask 앱

flask는 기본적으로 create_app()을 통해 application 객체를 만드는 것에서 시작한다.

이 프로젝트의 경우 __init__.py 파일에서 create_app()을 정의했다.

1. DB처리

'메인홈페이지의 새 책 데이터를 가져오기'를 예시로 설명한다.

 

1-1. .env 파일

먼저 .env 파일에 

DB_USER=root
DB_PASSWORD=
DB_HOST=localhost
DB_NAME=library_db
SECRET_KEY=

이런 식으로 SQL 서버에 들어가기 위해 필요한 값들을 저장해 놓는다.

 

1-2. __init__.py 파일

이 값들은 __init__.py에서 아래와 같은 코드를 사용해 app.config라는 dictionary에 저장된다.

    # .env 파일에서 환경 변수 불러오기
    app.config['DB_USER'] = os.environ.get('DB_USER')
    app.config['DB_PASSWORD'] = os.environ.get('DB_PASSWORD')
    app.config['DB_HOST'] = os.environ.get('DB_HOST')
    app.config['DB_NAME'] = os.environ.get('DB_NAME')
    app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')

    # db.py에서 사용할 DB 설정 저장
    app.config['DB_CONFIG'] = {
        'user': app.config['DB_USER'],
        'password': app.config['DB_PASSWORD'],
        'host': app.config['DB_HOST'],
        'database': app.config['DB_NAME']
    }

이 값들을 DB_CONFIG라는 새 dictionary에 한번에 저장해 sql 서버 연결이 용이해지게 한다.

 

1-3. db.py

이 파일의 get_db_connection()을 통해 sql 서버에 접근하고 이 연결 정보를 변수로 담아놓는다.

def get_db_connection():
    conn = mysql.connector.connect(**current_app.config['DB_CONFIG'])
    return conn

각 쿼리를 저장하는 함수들이 있고 이 함수들은 execute_query()를 통해 쿼리를 실행한다.

execute_query()는 서버와 연결 후 쿼리를 실행하고 연결을 종료하는 함수이다.

 

여기서 conn.cursor(dictionary=True)의

옵션이 없다면 (1, 'pw1', 1)과 같은 tuple로 값이 반환된다.

이 옵션이 있다면 [{'id': 1, 'pw': 'pw1', 'role': 1}]와 같이 가공이 더 쉬운 dictionary로 리턴된다.

만약 쿼리 실행 결과가 여러개라면 이 dictionary들의 list로 값이 반환된다.

def execute_query(query, params=None, fetch=None):
    conn = get_db_connection()
    cursor = conn.cursor(dictionary=True)
    cursor.execute(query, params or ())
   
    if fetch == 'all':
        result = cursor.fetchall()
    elif fetch == 'one':
        result = cursor.fetchone()
    else:
        conn.commit()
        result = None
        
    cursor.close()
    conn.close()
    return result

def get_newbooks():
    query = '''
        SELECT * FROM book_data
        ORDER BY book_code DESC
        LIMIT 6;
        '''
    return execute_query(query, fetch='all')

 

1-4. routes.py

위의 쿼리가 들어있는 함수들은 routes.py파일들에서 호출된다.

@main_bp.route('/')
def homepage():
    new_book_list = db.get_newbooks()
    
    return render_template('main/homepage.html', new_books = new_book_list)

여기서 new_book_list는 위에서 얘기한 dictionary들의 list가 된다.

이 render_template를 통해 html파일을 실행하고 인자도 전달된다.

 

1-5. html 파일

이 인자는 html파일 내부에서 

 

2. 웹요청 처리와 블루프린트

'auth' 관련 기능을 예시로 설명한다.

 

2-1. 블루프린트

auth_bp라는 변수에는 Blueprint라는 클래스의 객체가 저장된다.

이 객체는 auth기능과 관련된 모든 URL(라우팅) 규칙을 담는 컨테이너의 역할을 한다.

첫 번째 인자로 str을 받는데, 이는 나중에 url_for등을 사용할 때 이 blueprint를 찾기위한 이름이 된다.

뒤의 __name__은 module타입으로 현재 파일의 위치 정보를 담고 있다. 나중에 이 blueprint와 관련된 template나 static폴더의 위치를 자동 계산한다.

url_prefix는 이 blueprint(즉 auth_bp)안에 있는 모든 URL경로 앞에 '/auth'라는 주소를 붙인다.

 

auth_bp = Blueprint('auth', __name__, url_prefix='/auth')

 

2-2. __init__.py 선언

이 블루프린트들은 __init__.py 파일에서 등록을 해주어야한다.

    from .main import routes as main_routes
    from .auth import routes as auth_routes
    from .mypage import routes as mypage_routes
    
    app.register_blueprint(main_routes.main_bp)
    app.register_blueprint(auth_routes.auth_bp)
    app.register_blueprint(mypage_routes.mypage_bp)

 

2-3. routes.py

방금 auth_bp 변수는 라우팅 규칙을 담는다고 했는데,

아래 코드는 이 규칙을 추가하는 역할을 한다.

@auth_bp.route()는 바로 밑에 정의된 함수를 실행시킨다.

첫 인자인 '/login'은 url_prefix뒤에 붙는 세부 주소이다. 따라서 최종 로그인 페이지 주소는 .../auth/login이 된다.

뒤에 methods는 이 url에서 처리 가능한 요청의 종류를 정한다.

@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
	...
    return render_template('auth/login.html')


@auth_bp.route('/logout')
def logout():
	...
    return redirect(url_for('main.homepage'))

url_for()는 해당 url을 자동으로 생성해준다.

아래처럼 하드코딩된 경로를 사용하지 않아도 된다.

<a href="/auth/login">

인자는 '.'으로 구분되는데, 앞부분은 위에서 Blueprint의 첫 인자(str)이고

뒷부분은 함수 이름이다.

이 함수 이름은 routes파일들에서 @바로 밑에 나오는 함수를 의미한다.

위의 1-4의 코드를 참고하면 된다.

#1-4 코드
@main_bp.route('/')
def homepage():

결국 homepage()함수의 render_template()를 통해 최종적으로 html파일이 실행되게 된다.

 

3. css 및 js파일 연동

아래 코드에서도 url_for이 사용되는 것을 볼 수 있다.

다만 약간 기능이 달라지는데, 첫 인자인 'static'은 url_for의 예약어로,

static폴더를 찾으라는 의미이고 다음 인자에서 상세 경로가 주어진다.

파일구조 참고용

 

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">

<script src="{{ url_for('static', filename='js/slide.js') }}"></script>

 

4. 프로그램 전체 흐름

4-1. create_app() 실행

테스트환경에서 디버그 모드로 실행할 떄는 run.py를 실행하지만 run.py내부적으로도 create_app()이 실행된다.

실제 deploy 상태에서는 run.py가 아닌 직접 create_app()을 실행해야한다.

 

create_app()에서는 블루프린트 등록을 하고,

내부적으로 모든 url규칙(@...route(...))을 모아서 거대한 일종의 url map을 만든다.

 

4-2. 사용자의 접속

사용자가 url을 입력해 접속한다.

http://127.0.0.1:5000/

예시로 이 주소로 입력되었다면 '/'에 대한 요청을 찾는다.

 

4-3. url 매칭 및 함수 실행

flask는 url map에서 main 블루프린트의 homepage()가 '/'와 연결되어 있다는 것을 파악한다.따라서 app/main/routes.py의 homepage()를 실행한다.

 

4-4. 렌더링

homepage()는 render_template를 사용해 html코드를 생성하고 브라우저에 응답으로 보내준다.