본문 바로가기
카테고리 없음

비틀리(Bitly)와 같은 URL 단축 서비스 구현방법

by happytimes 2024. 5. 25.

비틀리(Bitly)와 같은 URL 단축 서비스는 사용자가 긴 URL을 짧은 형태로 변환해주고, 그 짧은 URL을 통해 원래의 긴 URL로 리다이렉션 해주는 기능을 제공합니다. 비틀리와 같은 서비스를 구현하기 위해 필요한 기본적인 단계를 설명하겠습니다.

 

서비스 구현방법

1. 프로젝트 설정 언어 및 프레임워크 선택: Python (Flask, Django), JavaScript (Node.js, Express), Java (Spring Boot) 등. 데이터베이스 선택: MySQL, PostgreSQL, MongoDB 등.

 

2. 데이터 모델 설계 URL 모델: 원본 URL, 단축된 URL, 생성된 날짜, 클릭 수 등.

 

3. URL 단축 알고리즘 단축된 URL을 생성하기 위한 알고리즘이 필요합니다. 보통 Base62(숫자와 대문자/소문자 알파벳) 인코딩을 사용합니다.

 

4. 주요 기능 구현

 

URL 단축

사용자가 긴 URL을 제출하면, 데이터베이스에 저장.

URL의 고유 ID를 가져와 Base62로 인코딩하여 단축된 URL 생성.

단축된 URL을 사용자에게 반환.

 

import string
import random

BASE62 = string.ascii_letters + string.digits

def encode(num):
    s = []
    while num:
        num, rem = divmod(num, 62)
        s.append(BASE62[rem])
    return ''.join(reversed(s))

 

 

URL 리다이렉션

 

사용자가 단축된 URL을 요청하면, 데이터베이스에서 해당 URL의 원본 URL을 검색.

원본 URL로 리다이렉션.

 

from flask import Flask, redirect
import sqlite3

app = Flask(__name__)

@app.route('/<short_url>')
def redirect_url(short_url):
    conn = sqlite3.connect('database.db')
    cursor = conn.cursor()
    cursor.execute("SELECT original_url FROM urls WHERE short_url = ?", (short_url,))
    result = cursor.fetchone()
    conn.close()
    
    if result:
        return redirect(result[0])
    else:
        return "URL not found", 404

 

5. 추가 기능 클릭 수 통계: URL이 몇 번 클릭되었는지 기록. 사용자 관리: 로그인 기능을 통해 사용자별 URL 관리. 만료 기간 설정: URL의 유효 기간을 설정.

 

6. 배포 서버 선택: AWS, Heroku, DigitalOcean 등. 도메인 설정: 짧은 도메인 이름 등록. 보안: HTTPS 적용, 데이터베이스 보안 설정.

 

예시: 전체 코드

 

from flask import Flask, request, redirect, jsonify
import sqlite3
import string
import random

app = Flask(__name__)

BASE62 = string.ascii_letters + string.digits

def encode(num):
    s = []
    while num:
        num, rem = divmod(num, 62)
        s.append(BASE62[rem])
    return ''.join(reversed(s))

def create_table():
    conn = sqlite3.connect('database.db')
    cursor = conn.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS urls
                      (id INTEGER PRIMARY KEY, original_url TEXT, short_url TEXT, clicks INTEGER)''')
    conn.commit()
    conn.close()

create_table()

@app.route('/shorten', methods=['POST'])
def shorten_url():
    original_url = request.json['url']
    conn = sqlite3.connect('database.db')
    cursor = conn.cursor()
    cursor.execute("INSERT INTO urls (original_url, clicks) VALUES (?, 0)", (original_url,))
    conn.commit()
    url_id = cursor.lastrowid
    short_url = encode(url_id)
    cursor.execute("UPDATE urls SET short_url = ? WHERE id = ?", (short_url, url_id))
    conn.commit()
    conn.close()
    return jsonify({'short_url': short_url})

@app.route('/<short_url>')
def redirect_url(short_url):
    conn = sqlite3.connect('database.db')
    cursor = conn.cursor()
    cursor.execute("SELECT original_url, clicks FROM urls WHERE short_url = ?", (short_url,))
    result = cursor.fetchone()
    if result:
        cursor.execute("UPDATE urls SET clicks = ? WHERE short_url = ?", (result[1] + 1, short_url))
        conn.commit()
        conn.close()
        return redirect(result[0])
    else:
        conn.close()
        return "URL not found", 404

if __name__ == '__main__':
    app.run(debug=True)

 

 

위 예제는 기본적인 URL 단축 및 리다이렉션 기능을 포함하고 있습니다. 이를 바탕으로 추가 기능을 구현하여 서비스를 확장할 수 있습니다.

 

데이터베이스 설정

비틀리와 같은 URL 단축 서비스를 구현할 때, 데이터베이스는 중요한 역할을 합니다. 여기서는 SQL 데이터베이스 (예: MySQL, PostgreSQL)와 NoSQL 데이터베이스 (예: MongoDB) 두 가지를 사용하는 방법을 설명하겠습니다. 1. SQL 데이터베이스 설정

 

1.1. 데이터베이스 테이블 설계 하나의 테이블을 사용하여 URL 정보를 저장할 수 있습니다. 테이블 스키마는 다음과 같습니다

 

CREATE TABLE urls (
    id SERIAL PRIMARY KEY,
    original_url TEXT NOT NULL,
    short_url VARCHAR(10) UNIQUE NOT NULL,
    clicks INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

 

 

1.2. Python과 SQL 데이터베이스 연동

Python의 SQLAlchemy와 같은 ORM(Object Relational Mapping)을 사용하면 데이터베이스와 쉽게 연동할 수 있습니다.

 

from flask import Flask, request, redirect, jsonify
from flask_sqlalchemy import SQLAlchemy
import string
import random

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://username:password@localhost/dbname'
db = SQLAlchemy(app)

BASE62 = string.ascii_letters + string.digits

class URL(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    original_url = db.Column(db.Text, nullable=False)
    short_url = db.Column(db.String(10), unique=True, nullable=False)
    clicks = db.Column(db.Integer, default=0)
    created_at = db.Column(db.DateTime, server_default=db.func.now())

def encode(num):
    s = []
    while num:
        num, rem = divmod(num, 62)
        s.append(BASE62[rem])
    return ''.join(reversed(s))

@app.route('/shorten', methods=['POST'])
def shorten_url():
    original_url = request.json['url']
    url = URL(original_url=original_url, clicks=0)
    db.session.add(url)
    db.session.commit()
    url.short_url = encode(url.id)
    db.session.commit()
    return jsonify({'short_url': url.short_url})

@app.route('/<short_url>')
def redirect_url(short_url):
    url = URL.query.filter_by(short_url=short_url).first_or_404()
    url.clicks += 1
    db.session.commit()
    return redirect(url.original_url)

if __name__ == '__main__':
    app.run(debug=True)

 

 

2. NoSQL 데이터베이스 설정

2.1. 데이터베이스 컬렉션 설계

MongoDB를 사용한다면, URL 정보를 저장하기 위한 컬렉션을 설계할 수 있습니다. 문서 구조는 다음과 같습니다:

 

{
    "_id": ObjectId,
    "original_url": "http://example.com",
    "short_url": "abc123",
    "clicks": 0,
    "created_at": ISODate("2024-05-23T00:00:00Z")
}

 

 

2.2. Python과 NoSQL 데이터베이스 연동

Python의 PyMongo를 사용하여 MongoDB와 연동할 수 있습니다.

from flask import Flask, request, redirect, jsonify
from pymongo import MongoClient
import string
import random
from datetime import datetime

app = Flask(__name__)

client = MongoClient('mongodb://localhost:27017/')
db = client['url_shortener']
urls = db['urls']

BASE62 = string.ascii_letters + string.digits

def encode(num):
    s = []
    while num:
        num, rem = divmod(num, 62)
        s.append(BASE62[rem])
    return ''.join(reversed(s))

@app.route('/shorten', methods=['POST'])
def shorten_url():
    original_url = request.json['url']
    url_id = urls.insert_one({
        "original_url": original_url,
        "clicks": 0,
        "created_at": datetime.utcnow()
    }).inserted_id
    short_url = encode(url_id.binary[0])
    urls.update_one({'_id': url_id}, {'$set': {'short_url': short_url}})
    return jsonify({'short_url': short_url})

@app.route('/<short_url>')
def redirect_url(short_url):
    url = urls.find_one_and_update(
        {'short_url': short_url},
        {'$inc': {'clicks': 1}},
        return_document=True
    )
    if url:
        return redirect(url['original_url'])
    else:
        return "URL not found", 404

if __name__ == '__main__':
    app.run(debug=True)