URL 단축 서비스 로직 구현
1. 요구조건 분석
•
https://도메인/{shortURL} 형태로 접근하면 사용자의 URL로 리다이렉트 시켜야 한다.
2. ShortPathRepository
import { supabase } from "../../lib/supabase.ts";
export class ShortPathRepository {
private generateRandomShortPath(): string {
const characters =
"ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789"; // 대문자 I, 소문자 l 제외
let result = "";
for (let i = 0; i < 6; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
result += characters[randomIndex];
}
return result;
}
private async isShortPathDuplicate(shortPath: string): Promise<boolean> {
const { data, error } = await supabase
.from("v1_short_paths")
.select("id")
.eq("short_path", shortPath)
.single();
if (error && error.code !== "PGRST116") {
// PGRST116: No rows found
console.error("Error checking short path duplication:", error);
throw new Error("Failed to check short path duplication");
}
return !!data; // 중복이면 true 반환
}
async insertShortPath(redirectURL: string): Promise<string> {
let shortPath = this.generateRandomShortPath();
// 중복되지 않는 shortPath 생성
while (await this.isShortPathDuplicate(shortPath)) {
shortPath = this.generateRandomShortPath();
}
const { error } = await supabase.from("v1_short_paths").insert({
short_path: shortPath,
redirect_url: redirectURL,
});
if (error) {
console.error("Error inserting short path:", error);
throw new Error("Failed to insert short path");
}
return shortPath;
}
async getRedirectURLByShortPath(shortPath: string): Promise<string | null> {
const { data, error } = await supabase
.from("v1_short_paths")
.select("redirect_url")
.eq("short_path", shortPath)
.single();
if (error) {
return null;
}
return data?.redirect_url || null;
}
}
TypeScript
복사
3. ShortPathService
import { ShortPathRepository } from "../repositories/shortPathRepository.ts";
export class ShortPathService {
private shortPathRepository: ShortPathRepository;
constructor() {
this.shortPathRepository = new ShortPathRepository();
}
async insertShortPath(redirectURL: string): Promise<string> {
return await this.shortPathRepository.insertShortPath(redirectURL);
}
async getRedirectURLByShortPath(shortPath: string): Promise<string | null> {
return await this.shortPathRepository.getRedirectURLByShortPath(shortPath);
}
}
TypeScript
복사
4. ShortPathController
import { ShortPathService } from "../services/shortPathService.ts";
import { Context } from "https://deno.land/x/hono/mod.ts";
export class ShortPathController {
private shortPathService: ShortPathService;
constructor() {
this.shortPathService = new ShortPathService();
}
async postShortPathV1(c: Context) {
try {
// 요청 본문에서 redirectURL 추출
const body = await c.req.json();
const redirectURL = body.redirectURL;
if (!redirectURL || typeof redirectURL !== "string") {
return c.json({ error: "Invalid or missing redirectURL" }, 400);
}
// ShortPath 생성
const shortPath =
await this.shortPathService.insertShortPath(redirectURL);
return c.json({ shortPath }, 201); // 생성된 shortPath 반환
} catch (error) {
console.error("Error in postShortPathV1:", error);
return c.json({ error: "Failed to create short path" }, 500);
}
}
async getRedirectURLByShortPathV1(c: Context) {
try {
// URL 파라미터에서 shortPath 추출
const shortPath = c.req.query("shortPath");
console.log(shortPath);
if (!shortPath || typeof shortPath !== "string") {
return c.json({ error: "Invalid or missing shortPath" }, 400);
}
// ShortPath로부터 redirectURL 조회
const redirectURL =
await this.shortPathService.getRedirectURLByShortPath(shortPath);
if (!redirectURL) {
return c.json({ error: "Short path not found" }, 404);
}
return c.json({ redirectURL }, 200); // 조회된 redirectURL 반환
} catch (error) {
console.error("Error in getRedirectURLByShortPathV1:", error);
return c.json({ error: "Failed to fetch redirect URL" }, 500);
}
}
}
TypeScript
복사
5. CROS
웹브라우저의 경우 현재의 도메인과 다른 도메인으로 API 요청시 CORS 오류가 발생할 수 있습니다. 도메인을 구매하여 사용하는 경우엔 문제없겠지만 보통 Vercel + Supabase 조합만으로 사용하는 경우엔 도메인이 달라 API 요청이 불가할 수 있습니다.
다른 도메인에서의 API 호출을 허용하기 위해 cors 추가 처리를 진행해야 합니다.
•
supabase/functions/api/index.ts
import { Hono } from "https://deno.land/x/hono/mod.ts";
import { shortPathRouter } from "./routers/shortPathRouter.ts";
import { cors } from "https://deno.land/x/hono/middleware.ts";
// Hono 인스턴스를 생성하여 애플리케이션을 설정합니다.
const app = new Hono();
app.use("*", cors());
// 기본 경로를 "/api"로 설정하고, "/users" 경로에 대해 userRouter를 설정합니다.
app.basePath("/api").route("/shortPath", shortPathRouter);
// 애플리케이션을 시작하여 요청을 수신합니다.
Deno.serve(app.fetch);
TypeScript
복사
Hono framework에서 지원하는 cors 설정을 적용합니다.
적용 후 bash scripts/deploy.sh 명령어로 Public 배포합니다.
6. Postman을 활용한 API 테스트
•
POST 요청
•
Database에 추가된 shortPath와 redirectURL
•
GET 요청
POST 요청을 통해 redirectURL을 등록하며, GET 요청을 통해 shortPath로 redirectURL을 조회 가능해졌습니다.
끝.