서버 관리 및 모니터링 프로젝트 (영화관 시스템)

고객의 두 번째 기능 구현

dmswo 2024. 12. 31. 13:19

고객의 두번 째 기능인 영화 이름으로 상영 예정인 영화 조회하기를 구현하겠다.

구현하면서 생긴 오류는 마지막에 적겠다.

1. 목표

로그인을 하면 각 사용자 유형에 따라 기능이 출력된다. user로 로그인 했다면 [1 사진]의 user_function.html으로 출력된다. 이 때 두번 째 기능을 선택하면 /movies/search 링크로 이동한다.

따라서 이 페이지의 기능을 구현하겠다.

1. 고객 기능

 

2. routes.py 구현

 

먼저 코드의 중요한 부분을 설명하겠다.

<라우트 정의>

 @app.route('/movies/search', methods=['GET', 'POST'])

 

  • /movies/search URL에 대한 요청을 처리한다.
  • methods=['GET', 'POST']: 이 라우트는 GET 요청(페이지 표시)과 POST 요청(폼 데이터 제출)을 모두 처리한다

<POST 요청 처리>

 def print_movie_title():
        if request.method == 'POST':
            movie_title = request.form['movie_title']
            result = get_movie_by_title(movie_title)
            if not result:
                flash(f"'{movie_title}'에 해당하는 영화가 없습니다. 다시 검색해 주세요.")
                return redirect(url_for('print_movie_title'))
            else:
                return render_template('user/movie_title_results_2.html', movies=result, search_title=movie_title)
  • request.form['movie_title] 은 HTML 폼에서 movie_title에 해당하는 사용자가 입력한 값을 가져온다. 즉 name="movie_title"으로 설정된 입력 필드에 사용자가 값을 입력하고 제출하면, 해당 데이터가 서버로 전송된다.
  • request.method == 'POST': 사용자가 검색 폼을 제출했을 때 실행.
  • get_movie_by_title(movie_title): 데이터베이스에서 해당 영화 제목을 검색하는 함수 호출.

<GET 요청 처리>

 return render_template('user/search_movie_2.html')

 

  • 사용자가 이 페이지(/movies/search)에 처음 접속하거나, 폼 제출 없이 요청(GET 요청)을 보낼 때 실행된다.
  • 검색 페이지(search_movie_2.html)를 렌더링하여 사용자에게 보여준다.

 

3. models.py 의 get_movie_by_title 함수 구현

영화 제목으로 데이터를 조회하는 함수

 

첫번째 코드로 했더니 문제 발생

3-1. SQL 쿼리에서 PostgreSQL에만 존재하는 pg_indexes 시스템 테이블을 사용함

3-2. SQLite에서는 ?를 사용하여 파라미터를 바인딩한다. %s는 psycopg2 (PostgreSQL)에 사용되는 구문이기 때문에 SQLite에서는 %s 대신 ?를 사용해야 한다.

3-3. 대소문자 구분을 하지 않기 위해 LOWER을 사용했다.

 

수정전 코드

def get_movie_by_title(movie_title):
    con = get_db_connection()
    cursor = con.cursor()
    # Check if the index already exists
    cursor.execute("SELECT EXISTS(SELECT 1 FROM pg_indexes WHERE indexname = 'idx_movie_title')")
    index_exists = cursor.fetchone()[0]

    if not index_exists:
         ursor.execute("CREATE INDEX idx_movie_title ON movie(movie_title)")
         con.commit()
    # Perform the query using the index
    cursor.execute("SELECT DISTINCT m.movie_id, movie_title, theater_id, screening_date, poster_url "
                       "FROM movie m, \"screeningschedule\" s "
                       "WHERE m.movie_id = s.movie_id AND screening_date >= current_date "
                       "AND m.movie_title = %s", (movie_title,))
    result = cursor.fetchall()

    return result

 

수정된 코드

def get_movie_by_title(movie_title):
    con = get_db_connection()
    cursor = con.cursor()

    # Check if the index already exists
    cursor.execute("PRAGMA index_list('movie')")
    indexes = cursor.fetchall()

    index_exists = any(index[1] == 'idx_movie_title' for index in indexes)

    if not index_exists:
         cursor.execute("CREATE INDEX idx_movie_title ON movie(movie_title)")
         con.commit()

    # Perform the query using the index
    cursor.execute("SELECT DISTINCT m.movie_id, movie_title, theater_id, screening_date "
                       "FROM movie m, \"screeningschedule\" s "
                       "WHERE m.movie_id = s.movie_id AND screening_date >= current_date "
                       "AND LOWER(m.movie_title) = LOWER(?)", (movie_title,))

    result = cursor.fetchall()

    return result

 

 

4. HTML 파일 

<HTML 파일 구조 수정>

사용자 별로 기능들과 html 파일이 많기 때문에 사용자 별로 폴더를 만들고 그 밑에 각 사용자의 기능에 해당하는 html파일을 넣었다. 

그리고 기능의 번호를 파일 이름 뒤에 추가했다.

 

movie_project/

    ├── templates/

                └── user/

                          └── print_all_movies_1.html

                          └── search_movie_2.html

                          └── movie_title_results_2.html

    └── staff

    └── analyst

    └── routes.py

    └── app.py

    └── models.py

 

1. search_movie_2.html: 사용자 입력을 받는 페이지 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>영화 검색</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f9;
            margin: 0;
            padding: 20px;
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        h1 {
            color: #333;
            text-align: center;
            margin-bottom: 20px;
        }

        form {
            background: #fff;
            padding: 20px;
            border: 1px solid #ddd;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            max-width: 400px;
            width: 100%;
        }

        label {
            display: block;
            margin-bottom: 10px;
            font-weight: bold;
            color: #555;
        }

        input[type="text"] {
            width: 100%;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            margin-bottom: 20px;
            font-size: 16px;
        }

        button {
            width: 100%;
            background-color: #007bff;
            color: #fff;
            border: none;
            padding: 10px 15px;
            font-size: 16px;
            border-radius: 4px;
            cursor: pointer;
        }

        button:hover {
            background-color: #0056b3;
        }

        ul {
            margin-top: 20px;
            padding: 0;
            list-style-type: none;
        }

        li {
            color: red;
            font-weight: bold;
            margin-bottom: 10px;
            text-align: center;
        }
    </style>
</head>
<body>
    <h1>영화 검색</h1>
    <form action="{{ url_for('print_movie_title') }}" method="POST">
        <label for="movie_title">영화 제목:</label>
        <input type="text" id="movie_title" name="movie_title" placeholder="영화 제목을 입력하세요" required>
        <button type="submit">검색</button>
    </form>

    {% with messages = get_flashed_messages() %}
        {% if messages %}
            <ul>
                {% for message in messages %}
                    <li>{{ message }}</li>
                {% endfor %}
            </ul>
        {% endif %}
    {% endwith %}
</body>
</html>

 

2. movie_title_results_2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>검색 결과</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f9;
            margin: 0;
            padding: 20px;
        }

        h1, h2 {
            text-align: center;
            color: #333;
        }

        ul {
            list-style-type: none;
            padding: 0;
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
        }

        li {
            background: #fff;
            margin: 10px;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            width: 300px;
            text-align: center;
        }

        img {
            max-width: 100%;
            height: auto;
            border-radius: 8px;
            margin-bottom: 10px;
        }

        a {
            display: inline-block;
            margin-top: 20px;
            text-decoration: none;
            background-color: #007bff;
            color: #fff;
            padding: 10px 20px;
            border-radius: 5px;
            text-align: center;
        }

        a:hover {
            background-color: #0056b3;
        }
    </style>
</head>
<body>
    <h1>검색 결과</h1>
    <h2>'{{ search_title }}' 검색 결과:</h2>
    <ul>
        {% for movie in movies %}
            <li>
                <img src="{{ movie[4] }}" alt="Poster for {{ movie[1] }}">
                <p><strong>영화 ID:</strong> {{ movie[0] }}</p>
                <p><strong>제목:</strong> {{ movie[1] }}</p>
                <p><strong>극장 ID:</strong> {{ movie[2] }}</p>
                <p><strong>상영 날짜:</strong> {{ movie[3] }}</p>
            </li>
        {% endfor %}
    </ul>
    <div style="text-align: center;">
        <a href="{{ url_for('print_movie_title') }}">다시 검색하기</a>
    </div>
</body>
</html>

 

5. 결과 화면

고객으로 로그인
2번째 기능 클릭
잘못된 영화 제목 입력시 오류 메세지 출력
정확한 제목을 입력했다면 해당 영화 출력

6. 오류

<오류1> 

'/movies/search'는 실제 경로가 아니라 URL을 정의할 때 사용하는 문자열로, url_for() 함수는 이를 인식할 수 없다. url_for()에 전달되는 값은 라우트 함수의 이름이어야 한다.

 

[오류1] 에서 빨간 부분을 url_for('print_movie_title') 로 바꾸기

[오류2]에서도 바꿨다.

오류1 (수정 전)
오류2 (수정완료)