Flask MySQL 로 URL 단축기 만들기

HTMLBeginner
지금 연습하기

소개

이 프로젝트는 Flask 와 MySQL 을 사용하여 간단한 URL 단축 서비스 (URL shortener service) 를 만드는 과정을 안내합니다. 데이터베이스 설정, 웹 인터페이스 디자인, URL 단축 기능 구현, 태그별 URL 검색, 분석 보기 등을 배우게 됩니다. 이 프로젝트는 초보자도 쉽게 따라 할 수 있으며, Python 과 데이터베이스 관리를 이용한 웹 개발에 대한 포괄적인 통찰력을 제공합니다.

이 프로젝트는 https://github.com/highoncarbs/shorty를 기반으로 하며, 원래 MIT 라이선스 하에 배포되었습니다.

👀 미리보기

사용자 정의 접미사 (custom suffix) 를 사용하여 URL 변환, 태그별 URL 검색, 링크 접근:

링크에 접근하는 데 사용된 운영 체제 및 플랫폼에 대한 정보 보기:

🎯 과제

이 프로젝트에서 다음을 배우게 됩니다:

  • Flask 애플리케이션을 MySQL 데이터베이스에 연결하는 방법
  • URL 정보를 저장하기 위한 MySQL 데이터베이스 스키마를 생성하고 관리하는 방법
  • 백엔드와 상호 작용하기 위해 HTML 및 CSS 를 사용하여 프론트엔드 웹 페이지를 구현하는 방법
  • 짧은 URL 을 생성하기 위해 Flask 에서 폼 데이터와 요청을 처리하는 방법
  • 짧은 URL 에서 원래 URL 로 사용자를 리디렉션하는 기능 개발 방법
  • 클릭 수 및 기본 브라우저/플랫폼 정보를 포함하여 URL 사용량을 추적하는 간단한 분석 기능 생성 방법
  • 사용자 경험을 향상시키기 위해 사용자 친화적인 오류 처리 및 사용자 정의 404 페이지를 디자인하는 방법

🏆 성과

이 프로젝트를 완료하면 다음을 수행할 수 있습니다:

  • 데이터베이스 및 테이블 생성, 데이터 삽입 및 쿼리를 포함하여 MySQL 로 기본 작업을 수행합니다.
  • 라우팅 (routing), 요청 처리 및 템플릿 렌더링을 포함하여 Flask 의 기본 사항을 이해합니다.
  • HTML 폼으로 작업하고 Flask 애플리케이션에서 데이터를 처리합니다.
  • 기본 프론트엔드 디자인 원칙을 적용하고 CSS 를 사용하여 시각적으로 매력적인 웹 인터페이스를 만듭니다.
  • URL 사용량에 대한 데이터를 수집하고 표시하기 위해 간단한 분석을 구현합니다.
  • 웹 애플리케이션에서 안정성과 사용자 경험을 향상시키기 위해 오류 처리에 대한 모범 사례를 구현합니다.
이것은 가이드 실험입니다. 학습과 실습을 돕기 위한 단계별 지침을 제공합니다.각 단계를 완료하고 실무 경험을 쌓기 위해 지침을 주의 깊게 따르세요. 과거 데이터에 따르면, 이것은 중급 레벨의 실험이며 완료율은 57%입니다.학습자들로부터 100%의 긍정적인 리뷰율을 받았습니다.

프로젝트 환경 설정

먼저, 터미널에서 프로젝트 환경을 설정해야 합니다. 여기에는 MySQL 연결을 위한 필수 Python 패키지 설치와 MySQL 서비스 시작이 포함됩니다. 서비스가 시작되면 데이터베이스와 URL 을 저장할 테이블을 생성합니다.

PyMySQL 설치:

pip install PyMySQL==1.1.0

MySQL 서비스 시작 및 MySQL 쉘에 로그인:

sudo service mysql start
mysql -u root

MySQL 쉘 내에서 다음 명령을 실행하여 데이터베이스와 테이블을 생성합니다:

데이터베이스 생성:

CREATE DATABASE IF NOT EXISTS SHORTY;
USE SHORTY;

테이블 생성:

CREATE TABLE IF NOT EXISTS WEB_URL
(
    ID             INT AUTO_INCREMENT,
    URL            VARCHAR(512),
    S_URL          VARCHAR(80),
    TAG            VARCHAR(80),
    COUNTER        INT DEFAULT 0,
    CHROME         INT DEFAULT 0,
    FIREFOX        INT DEFAULT 0,
    SAFARI         INT DEFAULT 0,
    OTHER_BROWSER  INT DEFAULT 0,
    ANDROID        INT DEFAULT 0,
    IOS            INT DEFAULT 0,
    WINDOWS        INT DEFAULT 0,
    LINUX          INT DEFAULT 0,
    MAC            INT DEFAULT 0,
    OTHER_PLATFORM INT DEFAULT 0,
    PRIMARY KEY (ID)
);

이 SQL 문은 단축 URL 및 관련 분석 정보를 저장하도록 설계된 SHORTY 데이터베이스에 WEB_URL이라는 테이블을 생성합니다.

  • CREATE TABLE IF NOT EXISTS WEB_URL: 이 명령은 데이터베이스에 이미 존재하지 않는 경우에만 WEB_URL이라는 새 테이블을 생성합니다. 이렇게 하면 스크립트를 여러 번 실행할 때 오류를 방지할 수 있습니다.
  • ID INT AUTO_INCREMENT: 이 열은 테이블의 기본 키로 지정되며 각 새 항목에 대해 자동으로 증가하도록 설정됩니다. 즉, 새 레코드가 추가될 때마다 MySQL 은 마지막 ID 에서 1 씩 증가하여 고유한 ID 를 자동으로 할당합니다.
  • URL VARCHAR(512): 이 열은 단축될 원래 URL 을 저장합니다. 데이터 유형 VARCHAR(512)는 최대 512 자까지 가변 길이 문자열을 저장할 수 있음을 의미합니다.
  • S_URL VARCHAR(80): 단축된 URL 문자열을 위한 열로, 최대 길이는 80 자입니다.
  • TAG VARCHAR(80): 이 열은 분류 또는 검색 목적으로 URL 과 관련된 태그를 저장하기 위한 것으로, 최대 길이는 80 자입니다.
  • COUNTER INT DEFAULT 0: 이 정수 열은 단축 URL 에 액세스한 횟수를 추적하는 데 사용될 가능성이 높습니다. 새 레코드를 생성할 때 기본값은 0 입니다.
    다음 여러 열은 단축 URL 에 대한 분석 데이터를 저장하도록 설계되었습니다:
  • CHROME, FIREFOX, SAFARI, OTHER_BROWSER: 이 열은 서로 다른 웹 브라우저에서 방문한 횟수를 추적하는 데 사용됩니다. 각 열은 기본값이 0 인 정수 열입니다.
  • ANDROID, IOS, WINDOWS, LINUX, MAC, OTHER_PLATFORM: 브라우저 열과 유사하게, 이 열은 서로 다른 운영 체제/플랫폼에서 방문한 횟수를 추적하기 위한 것으로, 각 열의 기본값은 0 입니다.
  • PRIMARY KEY (ID): 이 문장의 이 부분은 ID 열이 테이블의 기본 키임을 지정합니다. 기본 키는 테이블의 각 레코드에 대한 고유 식별자이며, 두 레코드가 동일한 ID를 갖지 않도록 보장합니다.

이 테이블 구조를 통해 단축 URL 에 대한 데이터 (액세스 빈도, 브라우저, 운영 체제 등) 를 저장, 검색 및 분석할 수 있습니다.

MySQL 쉘을 종료하려면 다음 명령을 실행할 수 있습니다:

EXIT;
✨ 솔루션 확인 및 연습

인덱스 페이지 템플릿 생성

인덱스 페이지는 사용자가 URL 단축 서비스와 상호 작용하는 곳입니다. HTML 과 Flask 의 템플릿 언어를 사용하여 동적 콘텐츠를 생성합니다.

templates/index.html에 다음 HTML 코드를 추가합니다:

<!doctype html>
<html>
  <head>
    <title>Shorty</title>
    <!-- Add Local SVG image when hosting. -->
    <link
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet"
    />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js"></script>
    <link rel="stylesheet" type="text/css" href="../static/skeleton.css" />
    <link rel="stylesheet" type="text/css" href="../static/normalize.css" />
    <link rel="stylesheet" type="text/css" href="../static/main.css" />
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:300,400,700,900"
      rel="stylesheet"
    />
  </head>
  <body>
    <div class="container  main_header">
      <h3 align="left"><a href="{{url_for('index')}}">Shorty</a></h3>
      <p>A very simple URL shortening service.</p>
    </div>

    <div class="u-full-width shorty">
      <div class="container"></div>
    </div>
  </body>
</html>

다음은 주요 구성 요소에 대한 개요입니다:

  • Google Material Icons: <link> 태그는 Google Material Icons 라이브러리를 가져와서, 보다 매력적인 사용자 인터페이스를 위해 미리 정의된 아이콘을 사용할 수 있도록 합니다.
  • Clipboard.js: <script> 태그는 클립보드에 콘텐츠를 복사하기 위한 인기 있는 JavaScript 라이브러리인 Clipboard.js 라이브러리를 포함합니다. 이는 URL 단축 서비스에서 유용할 수 있으며, 사용자가 단축된 URL 을 쉽게 복사할 수 있도록 합니다.
  • 스타일시트 (Stylesheets): 템플릿은 스타일 지정을 위해 여러 CSS 파일에 연결됩니다:
    • skeleton.css: 기본 스타일과 반응형 그리드 시스템을 제공하는 가벼운 CSS 프레임워크입니다.
    • normalize.css: 서로 다른 브라우저에서 일관된 스타일을 유지하기 위해 브라우저 기본 스타일을 재설정합니다.
    • main.css: "Shorty" 서비스에 특정한 사용자 정의 스타일을 포함합니다.
  • Google Fonts: 또 다른 <link> 태그는 Google Fonts 에서 "Roboto" 글꼴 패밀리를 가져와서, 타이포그래피 디자인을 위한 다양한 글꼴 가중치를 제공합니다.
  • <body> 내부에서 container main_header 클래스가 있는 div는 헤더 섹션을 생성하는 데 사용되며, 다음을 포함합니다:
    • Flask 의 url_for 함수를 통해 인덱스 페이지로 리디렉션되는 링크 (<a>) 가 포함된 제목 (<h3>), 'index' 경로에 대한 URL 을 동적으로 생성합니다.
    • "A very simple URL shortening service."로 서비스를 설명하는 단락 (<p>).
✨ 솔루션 확인 및 연습

URL 단축 및 태그 기반 검색 구현

이 단계에서는 URL 단축 및 태그 기반 URL 검색이라는 두 가지 주요 기능을 통합하여 웹 인터페이스를 개선합니다. 이를 통해 사용자는 URL 을 단축할 뿐만 아니라 태그를 사용하여 효율적으로 구성하고 검색할 수 있습니다.

URL 단축

먼저, 사용자가 단축하려는 URL 을 입력할 수 있는 양식을 인덱스 페이지에 생성합니다. 사용자 정의 접미사 및 태그에 대한 선택적 필드를 통해 개인화된 짧은 URL 과 보다 쉬운 관리를 위한 분류가 가능합니다.

templates/index.html에 다음 코드를 추가합니다:

<!-- Search URL block -->
<div class="search_url_block">
  <form method="post" action="/search" name="search_tag_block">
    <input type="text" name="search_url" placeholder="Search tags " />
    <button type="submit" class="button-primary search_url_btn" value="Search">
      Search
    </button>
  </form>
</div>
<!-- end block -->

첫 번째 코드 블록은 관련 태그로 URL 을 검색하도록 설계된 양식을 추가합니다:

  • search_url_block: 이 컨테이너는 검색 양식을 포함하여 페이지에서 더 나은 구성과 스타일 지정을 위한 별도의 섹션으로 만듭니다.
  • <form>: 검색 버튼을 클릭하면 /search 엔드포인트로 POST 요청을 제출하는 양식을 정의합니다. 이 양식은 식별을 위해 search_tag_block으로 명명됩니다.
  • <input>: 사용자가 검색하려는 태그를 입력할 수 있는 텍스트 입력 필드입니다. placeholder 속성은 필드의 목적에 대한 힌트를 사용자에게 제공합니다.
  • <button>: 양식 제출을 시작하는 제출 버튼입니다. button-primary 클래스는 CSS 에 정의된 특정 스타일을 추가할 가능성이 높습니다.

태그 기반 검색

URL 단축 기능을 보완하기 위해 사용자가 태그로 URL 을 검색할 수 있는 기능도 구현합니다. 이 기능은 인덱스 템플릿에 통합되어 사용자가 해당 URL 을 찾기 위해 태그를 입력할 수 있는 간단한 양식을 제공합니다.

templates/index.html에 다음 코드를 추가합니다:

<!-- URL Input block -->
<form method="post" action="" name="generate_block">
  <div class="row">
    <input type="text" name="url_input" placeholder="Enter URL" />
    <input type="text" name="url_custom" placeholder="Enter Custom Suffix" />
    <input type="text" name="url_tag" placeholder="Tag URL" />
    <button class="button-primary generate">Generate</button>
  </div>
</form>
<!-- end block -->

두 번째 코드 블록은 사용자가 URL 을 단축할 수 있는 양식을 소개하며, 사용자 정의를 위한 추가 필드가 있습니다:

  • 전체 양식은 method="post" 및 지정되지 않은 action 속성을 가진 <form> 요소로 래핑됩니다. 즉, 양식 데이터는 생성 버튼을 클릭하면 POST 요청을 통해 현재 URL 로 전송됩니다.
  • 양식 내부에서 row 클래스가 있는 <div>는 레이아웃 및 정렬에 도움이 되어 입력 필드와 버튼이 적절하게 구성되도록 합니다.
  • 세 개의 <input type="text"> 요소가 서로 다른 목적으로 제공됩니다:
    • 첫 번째 입력 필드는 사용자가 단축하려는 원래 URL 을 입력하는 데 사용됩니다.
    • 두 번째는 단축된 URL 에 대한 선택적 사용자 정의 접미사를 허용하여 사용자가 짧은 링크를 개인화할 수 있도록 합니다.
    • 세 번째 필드는 태그용으로 지정되어 사용자가 더 쉬운 검색 및 관리를 위해 단축된 URL 에 대한 설명 레이블을 범주화하거나 추가할 수 있습니다.
  • button-primary 클래스와 텍스트 "Generate"가 있는 <button>은 양식의 제출 버튼 역할을 합니다. 이 버튼을 클릭하면 제공된 정보로 단축된 URL 을 생성하기 위해 데이터가 서버 측 Flask 애플리케이션으로 전송됩니다.

이러한 개선 사항은 기본적인 URL 단축 기능을 넘어 더 많은 기능을 제공하여 사용자 경험을 크게 향상시킵니다. 태그로 URL 을 검색하는 기능은 단축된 링크를 효율적으로 관리하고 검색할 수 있도록 하며, 사용자 정의 접미사 및 태그를 추가하는 옵션은 URL 의 개인화 및 분류를 가능하게 합니다. 이 설정은 HTML 양식을 활용하여 사용자 입력을 수집하며, 이는 서버 측 Flask 애플리케이션에서 원하는 작업을 수행하기 위해 처리됩니다.

✨ 솔루션 확인 및 연습

오류 처리 및 단축 URL 표시

이 단계에서는 오류 메시지를 처리하고 단축된 URL 을 표시하여 사용자 경험을 향상시키는 데 중점을 둡니다.

오류 처리

먼저, 사용자에게 오류 메시지를 표시하는 기능을 추가합니다. 이는 사용자가 잘못된 URL 을 제출하거나 존재하지 않는 태그를 제출하는 경우와 같이 문제가 발생했을 때 피드백을 제공하는 데 매우 중요합니다. 적절한 오류 처리는 애플리케이션을 더욱 강력하고 사용자 친화적으로 만듭니다.

templates/index.html에 다음 코드를 추가합니다:

<!-- Error Display block -->
{% if error != '' %}
<p class="error_disp">{{error}}</p>
{% endif %} {% if shorty_url %}
<!-- end block-->

이 블록은 URL 단축 프로세스 중에 오류가 발생한 경우 사용자에게 피드백을 제공하도록 설계되었습니다:

  • {% if error != '' %}: Flask 의 템플릿 구문인 Jinja2 를 사용하는 조건문입니다. error 변수가 비어 있지 않은지 확인합니다. error 변수는 Flask 백엔드에서 템플릿으로 전달될 것으로 예상됩니다. 오류 메시지가 있으면 조건이 True로 평가됩니다.
  • <p class="error_disp">{{error}}</p>: 조건이 True일 때 이 단락 요소가 렌더링되어 error 변수에 포함된 오류 메시지를 표시합니다. error_disp 클래스는 오류 메시지의 스타일을 지정하여 사용자의 주의를 끌 수 있도록 시각적으로 구별하는 데 사용될 가능성이 높습니다.
  • {% endif %}: 이 조건 블록을 닫습니다. error 변수가 비어 있으면 이 블록의 아무것도 페이지에 렌더링되지 않습니다.

단축된 URL 표시

둘째, URL 이 성공적으로 단축된 후 단축된 URL 을 사용자에게 표시합니다. 여기에는 양식 제출 후 단축된 URL 을 동적으로 표시하는 인덱스 페이지의 섹션을 생성하는 작업이 포함되며, 쉽게 공유할 수 있도록 클립보드에 복사 버튼이 함께 제공됩니다.

templates/index.html에 다음 코드를 추가합니다:

<!-- URL Generator Display block -->
<div class="gen_block">
  <p class="gen_url">
    Shorty URL is
    <b><a id="short-url" href="{{shorty_url}}">{{shorty_url}}</a></b>
  </p>
  <button
    class="button-primary copy-btn"
    data-clipboard-action="copy"
    data-clipboard-target="#short-url"
  >
    Copy
  </button>
</div>
{% endif %}
<!-- end block -->

이 블록은 URL 이 성공적으로 단축되었을 때 표시되며 사용자가 단축된 URL 을 쉽게 복사할 수 있는 방법을 제공합니다:

  • {% if shorty_url %}: shorty_url 변수가 존재하고 비어 있지 않은지 확인하는 또 다른 조건문입니다. 이 변수는 서비스에서 생성된 단축된 URL 을 포함해야 합니다.
  • <p class="gen_url">: 이 단락은 단축된 URL 을 나타내는 메시지를 표시합니다. 그 안에서 <a id="short-url" href="{{shorty_url}}">{{shorty_url}}</a>는 단축된 URL 을 링크 텍스트와 href 속성으로 사용하여 사용자가 직접 클릭할 수 있는 하이퍼링크를 생성합니다.
  • <button>: 이 버튼은 단축된 URL 을 클립보드에 복사하여 쉽게 공유할 수 있도록 설계되었습니다. 스타일 지정 (button-primary) 및 기능 (copy-btn) 에 대한 클래스를 사용합니다. data-clipboard-action 속성은 수행할 작업 (복사) 을 지정하고 data-clipboard-target은 복사할 대상 요소 (단축된 URL 링크) 를 나타냅니다. 이 설정은 Clipboard.js(또는 유사한 라이브러리) 의 통합을 가정하여 복사 기능을 처리합니다.
  • {% endif %}: 단축된 URL 과 복사 버튼을 표시하기 위한 조건 블록을 닫습니다. shorty_url이 설정되지 않았거나 비어 있으면 이 섹션이 렌더링되지 않습니다.

이러한 개선 사항은 URL 단축 서비스의 유용성에 크게 기여합니다. 오류 메시지는 필수적인 피드백을 제공하여 사용자가 실수를 수정하거나 문제를 알 수 있도록 안내하는 반면, 쉬운 복사 기능과 함께 단축된 URL 을 표시하면 단축된 링크를 생성하고 공유하는 데 원활한 경험을 제공합니다.

✨ 솔루션 확인 및 연습

단축 URL 목록 및 관리

마지막 단계는 데이터베이스에 저장된 모든 단축된 URL 을 원래 URL, 받은 클릭 수 및 자세한 분석 링크와 함께 나열하는 섹션을 만드는 것입니다. 이 기능을 통해 사용자는 단축된 URL 을 한 곳에서 보고 관리할 수 있으므로 애플리케이션의 기능과 사용자 친화성이 향상됩니다.

templates/index.html에 다음 코드를 추가합니다:

        <!-- URL List Block -->
        {% if not error %}
        {% if not shorty_url %}
        <!-- Add Empty list case -> 'Wow . Such Empty!' -->
        <div style="overflow-x:auto;">
            <div class="table_list u-full-width">
                <table>
                    <thead>
                    <tr>
                        <th>Original URL</th>
                        <th>Short URL</th>
                        <th>Clicks</th>
                        <th>Info</th>
                    </tr>
                    </thead>

                    <tbody>
                    {% for url in table %}
                    <tr>
                        <td style="padding-left: 5px;">{{url[1]}}</td>
                        <td><a href="{{host+url[2]}}">{{ host+url[2]}}</a></td>
                        <td style="text-align: center;">{{url[4]}}</td>
                        <td id="info"><a href=" {{url_for('analytics' ,short_url=url[2])}} "><i class="material-icons">info_outline</i></a>
                        </td>
                    </tr>

                    {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
        {% endif %}
        {% endif %}
    </div>
</div>
<script type="text/javascript">
    var clipboard = new Clipboard('.copy-btn');
</script>
</html>

이 코드는 데이터베이스에 저장된 모든 단축된 URL 을 원래 URL, 단축된 URL, 클릭 수 및 자세한 분석 링크와 같은 정보를 제공하여 나열하도록 설계되었습니다. 구성 요소에 대한 자세한 분석은 다음과 같습니다:

  • 블록은 오류가 없을 때만 목록이 표시되도록 하는 조건 {% if not error %}로 시작합니다. 이는 특히 오류 메시지를 표시해야 할 때 깨끗한 사용자 인터페이스를 유지하는 데 도움이 됩니다.
  • 또 다른 조건 {% if not shorty_url %}은 새로 단축된 URL 이 방금 생성되지 않았는지 확인합니다. 이 조건은 새 URL 이 단축된 직후 목록이 표시되지 않도록 하여 사용자의 관심을 새로 생성된 단축된 URL 에 집중시키는 데 사용될 수 있습니다.
  • overflow-x:auto; 스타일은 더 작은 화면에서 테이블이 가로로 스크롤 가능하도록 div에 적용되어 반응성과 사용성을 향상시킵니다.
  • table 요소는 "Original URL", "Short URL", "Clicks" 및 "Info"에 대한 열 머리글을 정의하는 thead 섹션으로 구성됩니다. 이 레이아웃은 사용자에게 표시된 데이터를 이해하는 데 도움이 됩니다.
  • tbody 섹션은 Flask/Jinja2 루프: {% for url in table %}를 사용하여 동적으로 채워집니다. 이 루프는 Flask 백엔드에서 전달된 URL 모음 (table) 을 반복하여 각 URL 의 데이터를 새 테이블 행 (<tr>) 에 표시합니다.
  • 각 행 내에서 원래 URL 은 미적 목적을 위해 약간의 패딩과 함께 셀 (<td>) 에 표시됩니다. 단축된 URL 은 백엔드에서 전달되어야 하는 호스트 이름 (host) 에 단축된 경로 (url[2]) 를 추가하여 구성된 클릭 가능한 링크 (<a>) 로 표시됩니다.
  • "Clicks" 열은 단축된 URL 에 액세스한 횟수를 표시하며 가독성을 높이기 위해 가운데 정렬됩니다.
  • "Info" 열에는 각 단축된 URL 에 대한 분석 페이지로 연결되는 링크가 포함되어 있습니다. 이 링크는 Flask 의 url_for 함수를 사용하여 생성되며, 단축된 URL 을 매개변수로 사용하여 분석 경로에 대한 URL 을 동적으로 생성합니다. Google Material Icons 세트의 아이콘 (<i class="material-icons">info_outline</i>) 은 분석 링크를 시각적으로 나타내는 데 사용됩니다.
  • {% endfor %} 태그는 루프를 닫아 table 모음에 있는 각 URL 에 대해 행이 생성되도록 합니다.
  • 조건 블록은 {% endif %} 태그로 닫힙니다.
  • 맨 아래에 <script> 태그는 .copy-btn 클래스에 대해 Clipboard.js 를 초기화합니다. 이 클래스는 이전 섹션 (예: 단축된 URL 복사) 에서 사용되었을 수 있습니다. 이 스크립트는 copy-btn 클래스가 있는 모든 요소에 복사 - 클립보드 기능을 사용할 수 있도록 하여 Clipboard.js 라이브러리를 활용합니다.

이 마지막 단계는 사용자가 단축된 URL 에 대한 포괄적인 개요를 제공하여 링크를 더 쉽게 관리하고 분석할 수 있도록 함으로써 "Shorty" URL 단축 서비스의 기능을 완성합니다. Flask/Jinja2를 사용한 동적 데이터 렌더링의 통합과 신중한 UI 디자인의 결합은 사용자 친화적인 경험을 보장합니다.

✨ 솔루션 확인 및 연습

검색 페이지 템플릿 생성

사용자가 태그별로 URL 을 검색할 수 있도록 하려면 검색 페이지가 필요합니다.

templates/search.html에 다음 HTML 코드를 추가합니다:

<!DOCTYPE html>
<html>
<head>
    <title>Shorty</title>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons"
          rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="../static/skeleton.css">
    <link rel="stylesheet" type="text/css" href="../static/normalize.css">
    <link rel="stylesheet" type="text/css" href="../static/main.css">
    <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,700,900" rel="stylesheet">
</head>
<body>
<div class="container main_header">
    <h3 align="left"><a href="{{url_for('index')}}">Shorty</a></h3>
    <p>A dead simple URL shortener service.</p>
</div>
<!-- Search results  -->
<div class=" container search_header">
    <h4>Search Results for : <b> {{ search_tag }} <b></h4>

    <div style="overflow-x:auto;">
        <div class="table_list u-full-width">
            <table>
                <thead>
                <tr>
                    <th>Original URL</th>
                    <th>Short URL</th>
                    <th>Clicks</th>
                    <th>Info</th>

                </tr>
                </thead>

                <tbody>
                {% for url in table %}
                <tr>
                    <td style="padding-left: 5px;">{{url[1]}}</td>
                    <td><a href="{{ host+url[2]}}">{{host+url[2]}}</a></td>
                    <td style="text-align: center;">{{url[4]}}</td>
                    <td id="info"><a href=" {{url_for('analytics' ,short_url=url[2])}} "><i class="material-icons">info_outline</i></a>
                    </td>
                </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>
</body>
</html>

이 코드는 "Shorty" URL 단축 서비스의 검색 결과 페이지에 대한 템플릿을 생성합니다. 이 페이지는 사용자가 태그별로 URL 을 검색한 결과를 표시하도록 설계되었습니다. 템플릿에 대한 자세한 분석은 다음과 같습니다:

  • <body> 내에서 헤더 섹션 (<div class="container main_header">) 은 일관된 모양과 느낌을 제공하면서 인덱스 페이지를 미러링합니다. 여기에는 인덱스 페이지로 리디렉션되는 클릭 가능한 링크 ({{url_for('index')}}) 로 서비스 이름 ("Shorty") 과 서비스에 대한 간략한 설명이 포함됩니다.
  • <div class="container search_header"> 섹션은 검색 결과 영역을 소개하며, 검색에 사용된 태그 ({{ search_tag }}) 를 동적으로 표시하는 헤더 (<h4>) 로 시작합니다.
  • 결과는 가로 스크롤을 위해 스타일이 지정된 div 내의 테이블 (overflow-x:auto;) 에 표시되어 작은 화면의 장치에서 접근성을 보장합니다.
  • 테이블 구조 (<table>) 는 헤더 (<thead>) 와 본문 (<tbody>) 섹션으로 구성됩니다. 헤더는 인덱스 페이지에 표시된 목록과 유사하게 "Original URL", "Short URL", "Clicks" 및 "Info"에 대한 열을 정의합니다.
  • 테이블의 본문은 Flask 백엔드에서 전달된 URL 모음 (table) 을 반복하는 루프 ({% for url in table %}) 를 사용하여 동적으로 채워집니다. 각 반복은 URL 에 대한 테이블에서 새 행 (<tr>) 을 생성합니다.
    • 원래 URL 은 가독성을 높이기 위해 약간의 패딩과 함께 표시됩니다.
    • 단축된 URL 은 Flask 의 템플릿 구문을 사용하여 동적으로 삽입되는 호스트 이름에 단축된 경로를 추가하여 구성된 클릭 가능한 링크로 표시됩니다.
    • "Clicks" 열은 단축된 URL 에 액세스한 횟수를 표시하며 명확성을 위해 텍스트가 가운데 정렬됩니다.
    • "Info" 열은 Flask 의 url_for 함수를 사용하여 URL 을 동적으로 생성하고 시각적 표현을 위한 아이콘 (<i class="material-icons">info_outline</i>) 을 사용하여 각 URL 에 대한 자세한 분석 링크를 제공합니다.

이 검색 결과 템플릿은 사용자가 태그를 기반으로 URL 을 효과적으로 찾고 관리할 수 있도록 하여 원활하고 직관적인 사용자 경험을 제공함으로써 "Shorty" 서비스를 향상시킵니다. 인덱스 페이지의 스타일과 레이아웃 요소를 일관되게 사용하면 애플리케이션 전체에서 일관된 디자인을 보장합니다.

✨ 솔루션 확인 및 연습

분석 페이지 템플릿 생성

분석 페이지는 클릭 수, 브라우저 및 플랫폼 통계를 포함하여 단축 URL 사용에 대한 자세한 정보를 표시합니다.

templates/data.html에 다음 HTML 코드를 추가합니다:

<!doctype html>
<html>
  <head>
    <title>Shorty</title>

    <!-- Add Local SVG image when hosting. -->
    <link
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet"
    />

    <link rel="stylesheet" type="text/css" href="../static/skeleton.css" />
    <link rel="stylesheet" type="text/css" href="../static/normalize.css" />
    <link rel="stylesheet" type="text/css" href="../static/main.css" />
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:300,400,700,900"
      rel="stylesheet"
    />
  </head>
  <body>
    <div class="container  main_header">
      <h3 align="left"><a href="{{url_for('index')}}">Shorty</a></h3>
      <p>A dead simple URL shortener service.</p>
    </div>
    <div class=" container modal-content">
      <!-- Array index -> 
      broswer : 
          CHROME ,
          FIREFOX,
          SAFARI,
          OTHER_BROWSER,
      platform:
          ANDROID,
          IOS,
          WINDOWS,
          LINUX ,
          MAC,
          OTHER_PLATFORM
       -->
      <div class="url_info">
        <h4>
          Analytics data for :
          <a href="{{host+info[1]}}">{{'localhost/'+info[1]}}</a>
        </h4>
        <p>
          Original URL :
          <a style="text-decoration: none" href="{{info[0]}}">{{info[0]}}</a>
        </p>
      </div>
      <div class="data_block">
        <div class="browser_list">
          <h4>Browser</h4>
          <table>
            <thead>
              <tr>
                <th>Chrome</th>
                <th>Firefox</th>
                <th>Safari</th>
                <th>Other Broswers</th>
              </tr>
            </thead>

            <tbody>
              <tr>
                <td style="padding-left: 5px;">{{browser[0]}}</td>
                <td>{{browser[1]}}</td>
                <td>{{browser[2]}}</td>
                <td>{{browser[3]}}</td>
              </tr>
            </tbody>
          </table>
        </div>
        <div class="platform_list">
          <h4>Platform</h4>
          <table>
            <thead>
              <tr>
                <th>Android</th>
                <th>IOS</th>
                <th>Windows</th>
                <th>Linux</th>
                <th>Mac</th>
                <th>Other Platforms</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td style="padding-left: 5px;">{{platform[0]}}</td>
                <td>{{platform[1]}}</td>
                <td>{{platform[2]}}</td>
                <td>{{platform[3]}}</td>
                <td>{{platform[4]}}</td>
                <td>{{platform[5]}}</td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </body>
</html>

이 코드는 클릭 수, 브라우저 사용량 및 플랫폼 분포에 대한 정보를 포함합니다. 구조와 구성 요소에 대한 분석은 다음과 같습니다:

  • 클래스 container modal-content가 있는 div는 분석 정보를 캡슐화하며, 페이지 내에서 모달 또는 별도의 콘텐츠 블록으로 스타일이 지정됩니다.
  • url_info div는 다음을 표시합니다:
    • 특정 단축 URL 에 대한 분석 데이터가 관련되어 있음을 나타내는 <h4> 제목. 단축 URL 은 hostinfo[1] 변수를 사용하여 구성된 클릭 가능한 링크로 표시되며, 여기서 info[1]에는 단축된 경로가 포함됩니다.
    • 원래 콘텐츠에 대한 액세스를 용이하게 하는 클릭 가능한 링크로 원래 URL 을 표시하는 단락 (<p>).
  • browser_listplatform_list의 두 개의 별도 섹션은 각각 브라우저 및 플랫폼에 대한 사용 통계를 표시하는 테이블을 포함합니다:
    • browser_list 섹션에는 Chrome, Firefox, Safari 및 기타 브라우저에 대한 헤더가 있는 테이블이 포함되어 있습니다. <tbody> 섹션은 템플릿에 전달된 browser 배열 변수에서 가져온 해당 통계를 표시합니다.
    • 마찬가지로, platform_list 섹션에는 Android, iOS, Windows, Linux, Mac 및 기타 플랫폼에 대한 헤더가 있는 플랫폼 사용 테이블이 있습니다. 사용 통계는 platform 배열 변수에서 가져온 테이블 본문에 표시됩니다.
  • 테이블은 의미론적 HTML 을 보장하고 스타일 지정 및 접근성을 용이하게 하기 위해 헤더에 <thead>를 사용하고 실제 데이터에 <tbody>를 사용합니다.
  • 테이블 내의 데이터 셀 (<td>) 은 각 브라우저 및 플랫폼에 대한 해당 카운트를 표시하며, 가독성을 위해 정렬되고 스타일이 지정됩니다.

이 템플릿은 단축 URL 에 액세스하는 방식에 대한 통찰력을 제공하여 사용자가 분석 데이터를 효과적으로 전달하며, 여기에는 대상 사용자가 사용하는 브라우저 및 플랫폼이 포함됩니다. 깔끔한 레이아웃과 데이터를 별도의 섹션으로 명확하게 분리하여 사용자가 URL 의 성능을 쉽게 이해하고 해석할 수 있습니다.

✨ 솔루션 확인 및 연습

404 페이지 템플릿 생성

사용자 지정 404 페이지는 단축 URL 을 찾을 수 없을 때 더 유용한 오류 메시지를 제공하여 사용자 경험을 향상시킵니다.

templates/404.html에 다음 HTML 코드를 추가합니다:

<!doctype html>
<html>
  <head>
    <title>Shorty</title>

    <!-- Add Local SVG image when hosting. -->
    <link
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet"
    />

    <link rel="stylesheet" type="text/css" href="../static/skeleton.css" />
    <link rel="stylesheet" type="text/css" href="../static/normalize.css" />
    <link rel="stylesheet" type="text/css" href="../static/main.css" />
    <link
      href="https://fonts.googleapis.com/css?family=Roboto:300,400,700,900"
      rel="stylesheet"
    />
  </head>
  <body>
    <div class="lost">
      <h2>Oi , chap you seem lost !</h2>
    </div>
  </body>
</html>

이 사용자 지정 404 페이지 템플릿은 사용자가 찾고 있는 페이지가 존재하지 않음을 효과적으로 전달하는 동시에 가벼운 어조를 유지합니다.

✨ 솔루션 확인 및 연습

웹 인터페이스 디자인

다음으로, 웹 인터페이스를 디자인합니다. 애플리케이션의 스타일을 지정하기 위해 메인 CSS 파일을 생성하는 것으로 시작합니다.

static/main.css에 다음 CSS 코드를 추가하여 HTML 요소의 스타일을 지정합니다:

html {
  border-top: 5px solid #d9edf7;
}
body {
  font-family: "Roboto", sans-serif;
  margin-top: 50px;
  margin-bottom: 0;
}
h3 {
  padding: 0;
  margin: 0;
}
h3 a {
  font-weight: 700;
  text-decoration: none;
  color: black;
}
h3 a:hover {
  color: grey;
  transition: 0.2s all;
}
/** Main Header */
.main_header {
  margin-bottom: 20px;
}
/* Search Block */
.search_url_block {
  padding: 15px;
  background-color: #d9edf7;
  color: #31708f;
  border-radius: 5px;
  margin-bottom: 10px;
}
form {
  margin: 0;
}

.search_header {
  margin-top: 20px;
}

.material-icons {
  padding: 5px;
  padding-top: 7px;
  opacity: 0.7;
}

.material-icons:hover {
  opacity: 1;
}
/* Gen URL block */

.gen_block {
  margin-top: 10px;
  padding: 15px;
  background-color: #dff0d8;
  color: #2b542c;
  border-radius: 5px;
  width: auto;
  font-size: 20px;
}
/* Error Disp */
.error_disp {
  padding: 15px;
  border-radius: 5px;
  background-color: #fcf8e3;
  color: #b84442;
  width: auto;
  font-size: 20px;
  margin-top: 10px;
}

/* Table Display Block*/

.table_list {
  padding-top: 10px;
  margin-top: 40px;
  border-top: 2px solid lightgrey;
}
table {
  font-size: 20px;
  width: 100%;
}
thead {
  font-weight: 700;
  padding: 2px;
}
tbody {
  font-weight: 400;
}
th {
  padding: 5px;
}
td {
  padding: 5px;
}
tr {
  padding: 5px;
}
tbody tr:hover {
  background-color: #f5f5f5;
  transition: 0.1s all ease-out;
}

/* Analytics block*/
.url_info {
  margin-top: 10px;
  padding: 15px;
  background-color: #d9edf7;
  color: #31708f;
  border-radius: 5px;
  margin-bottom: 10px;
}
.url_info h4,
p {
  margin: 0;
  padding: 0;
}
.data_block {
  margin-top: 20px;
}

/* 404 . Lost*/
.lost {
  margin-top: 20px;
}
.lost h2 {
  font-weight: 700;
  font-size: 40px;
  text-align: center;

  color: #31708f;
}
.lost p {
  font-weight: 400;
  font-size: 20px;
  text-align: center;
}

전반적인 디자인은 배경색, 텍스트 색상 및 패딩을 조합하여 탐색하기 쉬운 깨끗하고 현대적인 인터페이스를 만듭니다. 다양한 요소에 걸쳐 border-radius 를 사용하면 인터페이스가 더 부드럽고 접근하기 쉬운 느낌을 주며, 호버 효과는 상호 작용성을 향상시킵니다. Roboto 글꼴을 일관되게 사용하면 애플리케이션 전체에서 일관된 모양을 유지합니다.

✨ 솔루션 확인 및 연습

데이터베이스 연결 및 기능 작동

유틸리티 함수는 데이터베이스 연결, URL 유효성 검사, 브라우저 및 플랫폼 카운터 업데이트, 짧은 URL 에 대한 임의 토큰 생성을 처리합니다.

데이터베이스 연결

먼저, pymysql을 사용하여 데이터베이스 연결을 설정합니다. 이는 Flask 애플리케이션이 MySQL 데이터베이스와 상호 작용할 수 있도록 하는 데 중요합니다.

utils.py에 다음 Python 코드를 추가합니다:

from urllib.parse import urlparse
import random
import string

import pymysql


db_config = {
    "host": "localhost",
    "user": "root",
    "password": "",
    "db": "SHORTY"
}


def get_db_connection() -> pymysql.Connection:
    """Create and return a new database connection."""
    return pymysql.connect(**db_config)
  • pymysql 라이브러리는 MySQL 데이터베이스에 연결하는 데 사용됩니다. 이 라이브러리를 사용하면 Python 애플리케이션이 간단한 API 를 사용하여 MySQL 데이터베이스와 상호 작용할 수 있습니다.
  • db_config 딕셔너리에는 호스트, 사용자, 비밀번호 및 데이터베이스 이름과 같은 데이터베이스 연결 매개변수가 포함되어 있습니다. 이는 더 나은 보안과 모듈성을 위해 별도의 config 모듈에서 가져옵니다. 구성 설정을 별도로 유지하면 기본 코드베이스를 수정하지 않고도 쉽게 조정할 수 있습니다.
  • get_db_connection() 함수는 pymysql.connect(**db_config) 함수를 사용하여 데이터베이스에 대한 새 연결을 생성하고 반환합니다. **db_config 구문은 딕셔너리를 키워드 인수로 언팩하는 데 사용됩니다.

데이터베이스 작동 함수

데이터베이스 연결을 설정한 후 다음 단계는 데이터베이스에서 작업을 수행하는 함수를 만드는 것입니다. 여기에는 주어진 짧은 URL 에 대한 데이터 나열과 브라우저 및 플랫폼 사용량에 따라 카운터 업데이트가 포함됩니다.

utils.py에 다음 Python 코드를 추가합니다:

def list_data(shorty_url: str) -> tuple:
    """
    Takes short_url for input.
    Returns counter , browser , platform ticks.
    """
    with get_db_connection() as conn, conn.cursor() as cursor:

        su = [shorty_url]
        info_sql = "SELECT URL , S_URL ,TAG FROM WEB_URL WHERE S_URL= %s; "
        counter_sql = "SELECT COUNTER FROM WEB_URL WHERE S_URL= %s; "
        browser_sql = "SELECT CHROME , FIREFOX , SAFARI, OTHER_BROWSER FROM WEB_URL WHERE S_URL =%s;"
        platform_sql = "SELECT ANDROID , IOS , WINDOWS, LINUX , MAC , OTHER_PLATFORM FROM WEB_URL WHERE S_URL = %s;"

        cursor.execute(info_sql, su)
        info_fetch = cursor.fetchone()
        cursor.execute(counter_sql, su)
        counter_fetch = cursor.fetchone()
        cursor.execute(browser_sql, su)
        browser_fetch = cursor.fetchone()
        cursor.execute(platform_sql, su)
        platform_fetch = cursor.fetchone()

    return info_fetch, counter_fetch, browser_fetch, platform_fetch

def update_counters(cursor: pymysql.Connection, short_url: str, browser_dict: dict, platform_dict: dict) -> None:
    """Update browser and platform counters in the database for the given short_url."""
    counter_sql = """UPDATE WEB_URL SET COUNTER = COUNTER + 1,
                     CHROME = CHROME + %s, FIREFOX = FIREFOX + %s, SAFARI = SAFARI + %s, OTHER_BROWSER = OTHER_BROWSER + %s,
                     ANDROID = ANDROID + %s, IOS = IOS + %s, WINDOWS = WINDOWS + %s, LINUX = LINUX + %s, MAC = MAC + %s, OTHER_PLATFORM = OTHER_PLATFORM + %s
                     WHERE S_URL = %s;"""
    cursor.execute(counter_sql, (browser_dict['chrome'], browser_dict['firefox'], browser_dict['safari'], browser_dict['other'],
                                 platform_dict['android'], platform_dict['iphone'], platform_dict[
                                     'windows'], platform_dict['linux'], platform_dict['macos'], platform_dict['other'],
                                 short_url))
  • list_data(shorty_url) 함수는 주어진 짧은 URL(shorty_url) 에 대해 WEB_URL 테이블에서 다양한 정보를 검색하도록 설계되었습니다. 데이터베이스에 연결하고 네 개의 SQL 쿼리를 실행하여 원래 URL, 관련 태그, 액세스 카운터, 브라우저 및 플랫폼 분석을 가져옵니다. 각 쿼리는 별도로 실행되고 결과가 가져와 반환됩니다.
  • update_counters(cursor, short_url, browser_dict, platform_dict) 함수는 데이터베이스에서 주어진 짧은 URL 에 대한 액세스 카운터, 브라우저 및 플랫폼 카운트를 업데이트합니다. 이 함수는 데이터베이스 커서, 짧은 URL, 각 브라우저 및 플랫폼에 대한 카운트를 포함하는 두 개의 딕셔너리를 인수로 사용합니다. 제공된 데이터를 기반으로 짧은 URL 에 대한 카운터를 증가시키기 위해 UPDATE SQL 문을 구성합니다. 이 함수는 브라우저 및 플랫폼 정보가 이 함수를 호출하기 전에 결정되었고, 이 정보가 WEB_URL 테이블의 열에 해당하는 키가 있는 딕셔너리 (browser_dictplatform_dict) 로 전달된다고 가정합니다.

이 설정은 Flask 및 MySQL 을 사용하는 웹 애플리케이션의 일반적인 패턴으로, 일반적인 데이터베이스 작업에 대한 유틸리티 함수가 정의됩니다. 그런 다음 이러한 함수를 애플리케이션 전체에서 가져와 사용하여 데이터베이스와 상호 작용하여 직접적인 데이터베이스 액세스 및 SQL 쿼리를 기본 애플리케이션 로직에서 추상화할 수 있습니다.

✨ 솔루션 확인 및 연습

데이터베이스 관련 없는 유틸리티 함수 구현

마지막으로, 데이터베이스와 상호 작용하지 않는 유틸리티 함수를 구현합니다. 여기에는 짧은 URL 에 대한 임의 토큰 생성 및 URL 유효성 검사가 포함됩니다.

utils.py에 다음 Python 코드를 추가합니다:

def random_token(size: int = 6) -> str:
    """
    Generates a random string of 6 chars , use size argument
    to change the size of token.
    Returns a valid token of desired size ,
    *default is 6 chars
    """
    BASE_LIST = string.digits + string.ascii_letters

    token = ''.join((random.choice(BASE_LIST)) for char in range(size))
    return token


def url_check(url: str) -> bool:
    """
    Expects a string as argument.
    Retruns True , if URL is valid else False.
    For detailed docs look into urlparse.
    """
    try:
        result = urlparse(url)
        if all([result.scheme, result.netloc]):
            return True
        else:
            return False
    except:
        return False
  • random_token 함수는 짧은 URL 을 생성하기 위한 토큰 역할을 하는 임의 문자열을 생성합니다. 기본적으로 6 자 토큰을 생성하지만 size 인수를 사용하여 조정할 수 있습니다.
    • 토큰을 생성하기 위한 기본 목록으로 숫자 (string.digits) 와 문자 (string.ascii_letters) 의 조합을 사용합니다.
    • 토큰은 size 매개변수에 지정된 횟수만큼 BASE_LIST에서 문자를 무작위로 선택하여 생성됩니다. 이는 리스트 컴프리헨션과 random.choice()를 사용하여 수행되며, 지정된 시퀀스에서 임의의 요소를 선택합니다.
    • 생성된 토큰은 문자열로 반환됩니다. 이 토큰은 짧은 URL 식별자로 사용할 수 있습니다.
  • url_check 함수는 주어진 URL 이 올바른 형식인지 확인하기 위해 유효성을 검사합니다.
    • urllib.parse 모듈의 urlparse 함수를 사용하여 주어진 URL 을 구성 요소로 구문 분석합니다.
    • 이 함수는 URL 에 스키마 (예: http, https) 와 netloc(네트워크 위치, 예: www.example.com) 이 모두 있는지 확인합니다. 둘 다 URL 이 유효한 것으로 간주되기 위한 필수 구성 요소입니다.
    • 두 구성 요소가 모두 있으면 함수는 True를 반환하여 URL 이 유효함을 나타냅니다. 둘 중 하나가 없거나 구문 분석 중에 예외가 발생하면 (예: 입력이 문자열이 아닌 경우) 함수는 False를 반환합니다.
    • 이 유효성 검사는 유효한 URL 만 애플리케이션에서 처리하고 저장하도록 하는 데 중요합니다.

이러한 유틸리티 함수는 URL 단축 서비스에 필수적이며, 단축된 URL 에 대한 고유 식별자를 생성하고 유효한 URL 만 시스템에서 허용되도록 하는 데 필요한 기능을 제공합니다.

✨ 솔루션 확인 및 연습

인덱스 라우트 초기화 및 구현

마지막으로, URL 단축기의 핵심인 Flask 애플리케이션을 구축합니다.

인덱스 경로를 설정하는 것으로 시작합니다. 이 경로는 메인 페이지를 제공하고 GET 및 POST 요청을 모두 처리합니다. GET 요청 시에는 모든 단축된 URL 을 표시합니다. POST 요청 시에는 단축할 새 URL 을 수락하고, 고유 토큰을 생성하여 데이터베이스에 저장합니다.

app.py에 다음 Python 코드를 추가합니다:

from flask import Flask, request, redirect, render_template, make_response

from utils import get_db_connection, list_data, random_token, update_counters, url_check


app = Flask(__name__)


@app.route('/', methods=['GET', 'POST'])
def index():
    with get_db_connection() as conn, conn.cursor() as cursor:
        ## Fetch all data to display on index
        cursor.execute("SELECT * FROM WEB_URL;")
        result_all_fetch = cursor.fetchall()

        if request.method == 'POST':
            og_url = request.form.get('url_input')
            custom_suff = request.form.get('url_custom', '')
            tag_url = request.form.get('url_tag', '')

            token_string = random_token() if not custom_suff else custom_suff

            if og_url and url_check(og_url):
                cursor.execute(
                    "SELECT S_URL FROM WEB_URL WHERE S_URL = %s FOR UPDATE", (token_string,))
                if cursor.fetchone() is None:
                    cursor.execute(
                        "INSERT INTO WEB_URL(URL, S_URL, TAG) VALUES(%s, %s, %s)", (og_url, token_string, tag_url))
                    conn.commit()
                    return render_template('index.html', shorty_url=f"{shorty_host}{token_string}")
                else:
                    error = "The custom suffix already exists. Please use another suffix or leave it blank for a random one."
            else:
                error = "Invalid URL provided. Please enter a valid URL."

            return render_template('index.html', table=result_all_fetch, host=shorty_host, error=error)

        return render_template('index.html', table=result_all_fetch, host=shorty_host)

app.py 내의 index 함수는 Python 으로 작성된 마이크로 웹 프레임워크인 Flask 를 사용하여 URL 단축기 서비스의 핵심 기능을 설정합니다. 이 함수는 애플리케이션의 인덱스 경로 (/) 가 어떻게 동작하고 GET 및 POST 요청을 모두 처리하는지 정의합니다. 다음은 코드에 대한 자세한 설명입니다:

Flask 설정

  • Flask 애플리케이션은 app = Flask(__name__)으로 초기화됩니다.
  • @app.route('/', methods=['GET', 'POST']) 데코레이터는 index 함수가 루트 URL(/) 에 대한 요청을 처리하고 GET 및 POST 메서드를 모두 허용하도록 지정합니다.

GET 요청 처리

  • 함수가 GET 요청을 받으면 get_db_connection() 유틸리티 함수를 사용하여 데이터베이스에 연결하고 WEB_URL 테이블에서 모든 레코드를 가져옵니다. 이는 인덱스 페이지에 모든 단축된 URL 을 표시하기 위한 것입니다.
  • 가져온 레코드는 호스트 URL(shorty_host) 과 함께 render_template 함수에 전달되어 index.html 템플릿을 렌더링합니다. 이 템플릿에는 각 단축된 URL 과 해당 세부 정보를 표시하는 테이블 또는 목록이 포함될 가능성이 높습니다.

POST 요청 처리

  • POST 요청의 경우, 일반적으로 인덱스 페이지의 양식에서 제출되며, 함수는 원래 URL(og_url), 선택적 사용자 지정 접미사 (custom_suff), 선택적 태그 (tag_url) 를 양식 데이터에서 추출합니다.
  • 그런 다음 사용자 지정 접미사가 제공되지 않은 경우 random_token()을 사용하여 단축된 URL 에 대한 임의 토큰을 생성합니다. 이 토큰 (또는 사용자 지정 접미사) 은 단축된 URL 에 대한 고유 식별자 역할을 합니다.
  • 진행하기 전에 함수는 url_check(og_url)를 사용하여 원래 URL 의 유효성을 검사합니다. URL 이 유효하고 토큰/접미사가 고유한 경우 (데이터베이스에 아직 없는 경우) 원래 URL, 토큰/접미사를 짧은 URL 로, 태그를 사용하여 새 레코드가 WEB_URL 테이블에 삽입됩니다.
  • 작업이 성공하면 함수는 index.html 템플릿을 다시 렌더링하여 새로 단축된 URL(shorty_url) 을 모든 기존 레코드와 함께 표시합니다.
  • 토큰/접미사가 이미 데이터베이스에 있거나 원래 URL 이 유효하지 않은 경우 오류 메시지가 설정되고 index.html 템플릿이 오류 메시지 및 기존 레코드와 함께 렌더링됩니다.

오류 처리 및 템플릿 렌더링

  • render_template 함수는 모든 레코드 목록 (table), 단축된 링크의 기본 호스트 URL(host), 새로 단축된 URL(shorty_url), 모든 오류 메시지 (error) 와 같은 다양한 매개변수를 전달하여 index.html 템플릿을 광범위하게 렌더링하는 데 사용됩니다. 이 함수는 Python 변수를 HTML 템플릿에 삽입하여 Flask 에서 동적 콘텐츠 렌더링을 용이하게 합니다.

이 Flask 애플리케이션은 URL 단축기 서비스의 백엔드 역할을 하며, 데이터베이스 상호 작용, URL 유효성 검사 및 토큰 생성을 처리하는 동시에 사용자 상호 작용 및 데이터베이스 내용에 따라 웹 페이지를 동적으로 생성합니다.

✨ 솔루션 확인 및 연습

리다이렉션 라우트 및 분석 라우트 구현

리디렉션 경로

다음으로, 짧은 URL 에서 원래 URL 로의 리디렉션을 처리하는 경로를 만듭니다. 이 경로는 짧은 URL 을 캡처하고, 데이터베이스에서 조회하여 사용자를 원래 URL 로 리디렉션합니다.

app.py에 다음 코드를 추가합니다:

@app.route('/<short_url>')
def reroute(short_url):
    with get_db_connection() as conn, conn.cursor() as cursor:
        platform = request.user_agent.platform or 'other'
        browser = request.user_agent.browser or 'other'
        browser_dict = {'firefox': 0, 'chrome': 0, 'safari': 0, 'other': 0}
        platform_dict = {'windows': 0, 'iphone': 0,
                         'android': 0, 'linux': 0, 'macos': 0, 'other': 0}

        ## Increment browser and platform counters
        browser_dict[browser] = browser_dict.get(browser, 0) + 1
        platform_dict[platform] = platform_dict.get(platform, 0) + 1

        cursor.execute(
            "SELECT URL FROM WEB_URL WHERE S_URL = %s;", (short_url,))
        try:
            new_url = cursor.fetchone()[0]
            update_counters(cursor, short_url, browser_dict, platform_dict)
            conn.commit()
            return redirect(new_url)
        except Exception:
            return render_template('404.html'), 404
  • @app.route('/<short_url>') 데코레이터는 루트 URL 다음에 오는 모든 경로 세그먼트에 일치하는 동적 경로를 생성합니다. 이 세그먼트 (short_url) 는 reroute 함수에 인수로 전달됩니다.
  • 함수 내부에서는 데이터베이스 연결을 설정하고 요청이 발생한 브라우저 및 플랫폼을 추적하기 위한 딕셔너리를 초기화합니다. Flask 의 request.user_agent를 사용하여 브라우저 및 플랫폼을 결정합니다.
  • 그런 다음 SQL 쿼리를 실행하여 주어진 short_url과 관련된 원래 URL 을 찾습니다. 찾으면 update_counters 함수를 사용하여 데이터베이스에서 브라우저 및 플랫폼에 대한 카운터를 업데이트합니다.
  • 데이터베이스에 변경 사항을 커밋한 후, 함수는 Flask 의 redirect 함수를 사용하여 사용자를 원래 URL 로 보냅니다.
  • short_url이 데이터베이스에서 발견되지 않거나 다른 예외가 발생하면 함수는 render_template('404.html')을 사용하여 404 오류 페이지를 렌더링합니다.

분석 경로

특정 짧은 URL 에 대한 자세한 정보 (예: 액세스 횟수, 플랫폼 및 브라우저) 를 표시하는 분석 경로를 만듭니다.

app.py에 다음 코드를 추가합니다:

@app.route('/analytics/<short_url>')
def analytics(short_url):
    info_fetch, counter_fetch, browser_fetch, platform_fetch = list_data(
        short_url)
    return render_template("data.html", host=shorty_host, info=info_fetch, counter=counter_fetch, browser=browser_fetch, platform=platform_fetch)
  • @app.route('/analytics/<short_url>') 데코레이터는 특정 짧은 URL 에 대한 분석에 액세스하기 위한 경로를 정의합니다. short_url은 URL 경로에서 캡처되어 analytics 함수에 전달됩니다.
  • 함수는 list_data(short_url)을 호출하여 짧은 URL 에 대한 정보 (액세스 횟수, 다양한 브라우저 및 플랫폼에서의 액세스 분포 등) 를 데이터베이스에서 쿼리합니다.
  • 가져온 데이터 (info_fetch, counter_fetch, browser_fetch, platform_fetch) 는 호스트 URL(shorty_host) 과 함께 render_template 함수에 전달되어 data.html 템플릿을 렌더링합니다. 이 템플릿은 테이블 또는 차트와 같은 사용자 친화적인 형식으로 분석 데이터를 표시할 가능성이 높습니다.

이러한 경로는 URL 단축기 서비스의 기능을 확장하여 사용자를 짧은 URL 에서 원래 대상으로 리디렉션할 뿐만 아니라 해당 짧은 URL 이 어떻게 사용되고 있는지 (액세스 횟수, 링크에 액세스하는 장치 및 브라우저 유형 포함) 에 대한 통찰력을 제공합니다. 이 정보는 공유된 URL 의 도달 범위와 영향을 이해하는 데 유용할 수 있습니다.

✨ 솔루션 확인 및 연습

검색 라우트 구현 및 애플리케이션 실행

검색 경로

사용자가 태그로 단축된 URL 을 찾을 수 있도록 검색 경로를 설정합니다. 이 경로는 GET 및 POST 요청을 모두 처리하여 검색 양식을 표시하고 검색 쿼리를 처리합니다.

app.py에 다음 코드를 추가합니다:

@app.route('/search', methods=['GET', 'POST'])
def search():
    s_tag = request.form.get('search_url', '')
    if not s_tag:
        return render_template('index.html', error="Please enter a search term.")

    with get_db_connection() as conn, conn.cursor() as cursor:
        cursor.execute("SELECT * FROM WEB_URL WHERE TAG = %s", (s_tag,))
        search_tag_fetch = cursor.fetchall()
        return render_template('search.html', host=shorty_host, search_tag=s_tag, table=search_tag_fetch)
  • @app.route('/search', methods=['GET', 'POST']) 데코레이터는 /search 경로를 설정하여 GET 및 POST 요청을 모두 처리합니다. GET 요청은 검색 양식을 표시하는 데 사용될 수 있지만, POST 요청은 사용자가 제출한 양식 데이터를 처리합니다.
  • search 함수 내부에서 request.form.get('search_url', '')는 제출된 양식 데이터에서 검색어를 검색하려고 시도합니다. 두 번째 매개변수 ('') 는 'search_url'이 없는 경우의 기본값이며, 이러한 경우 검색어를 빈 문자열로 만듭니다.
  • 검색어가 제공되지 않은 경우 (not s_tag), 함수는 사용자에게 검색어가 필요하다는 오류 메시지와 함께 인덱스 페이지로 리디렉션합니다.
  • 검색어가 제공된 경우, 함수는 데이터베이스에 연결하고 WEB_URL 테이블에서 TAG 열이 제공된 검색어와 일치하는 모든 레코드를 찾는 SQL 쿼리를 실행합니다. 이를 통해 사용자는 특정 태그와 관련된 모든 단축된 URL 을 찾을 수 있습니다.
  • 이 쿼리의 결과 (search_tag_fetch) 는 호스트 URL(shorty_host) 및 검색어 (s_tag) 와 함께 render_template 함수에 전달되어 search.html 템플릿을 렌더링합니다. 이 템플릿은 결과를 목록 또는 테이블 형식으로 표시하여 사용자가 일치하는 모든 레코드를 보고 상호 작용할 수 있도록 합니다 (예: 원래 URL 방문, 분석 보기).

애플리케이션 실행

마지막으로, Flask 애플리케이션을 실행하는 코드를 추가합니다. 여기에는 애플리케이션이 수신 대기할 호스트 및 포트 설정과 환경 변수 지정이 포함됩니다.

app.py의 맨 아래에 다음 코드를 추가합니다:

shorty_host = "https://****.labex.io/"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

참고: shorty_host의 URL 을 현재 환경의 URL 로 바꿔야 합니다. Web 8080 탭으로 전환하여 찾을 수 있으며, URL 은 /로 끝나야 합니다.

Web 8080 탭 URL

이제 다음 명령을 사용하여 프로젝트를 실행할 수 있습니다:

python app.py

"Web 8080" 탭으로 전환하고 웹 페이지를 다시 로드하여 다음 효과를 확인하십시오.

사용자 정의 접미사 유무에 관계없이 URL 변환, 태그별 URL 검색, 링크 액세스:

링크에 액세스하는 데 사용된 운영 체제 및 플랫폼에 대한 정보 보기:

✨ 솔루션 확인 및 연습

요약

URL 단축기 프로젝트를 완료하신 것을 축하드립니다! 이 도구는 더 짧고 관리하기 쉬운 링크를 생성하여 긴 URL 을 공유하는 것을 단순화합니다. 하지만, 염두에 두어야 할 몇 가지 중요한 고려 사항이 있습니다:

  1. 기본 URL 길이: 단축기 자체의 기본 URL 이 긴 경우, 결과적으로 단축된 URL 이 예상만큼 짧지 않을 수 있습니다 (프로젝트에서와 같이). 단축된 URL 의 총 길이는 기본 URL 을 포함하며, 이는 단축기의 효과를 제한할 수 있습니다. 가장 간결한 URL 을 얻으려면 서비스에 더 짧은 기본 URL 또는 도메인 이름을 사용하는 것을 고려하십시오.

  2. 웹사이트 임베딩 제한: "프레임에 'https://scholar.google.com/'을 표시하는 것을 거부했습니다. 'X-Frame-Options'을 'sameorigin'으로 설정했기 때문입니다."와 같은 문제가 발생할 수 있습니다. 이 오류는 일부 웹사이트가 X-Frame-Options HTTP 헤더를 사용하여 다른 도메인의 iframe 에 콘텐츠가 표시되는 것을 방지하여 "클릭재킹 (clickjacking)" 공격에 대한 보안을 강화하기 때문에 발생합니다. 사이트가 이 헤더를 sameorigin으로 설정하면 동일한 출처를 공유하는 iframe 으로의 임베딩을 제한합니다. 이것은 URL 단축기의 제한 사항이 아니라 대상 웹사이트에서 구현된 보안 조치입니다. 원활한 리디렉션을 위해서는 단축된 URL 을 iframe 에 임베딩하는 대신 직접 링크로 사용하는 것이 가장 좋습니다.

이러한 고려 사항은 웹 개발에서 편의성과 보안 간의 균형과 최적의 도구 사용을 위한 사용자 인식의 중요성을 강조합니다.