
로그인 없이 좋아요 기능 구현하기 (Supabase + IP 기반)
블로그를 운영하다 보면, 방문자들의 피드백을 확인할 수 있는 가장 간단한 방법 중 하나가 ‘좋아요 버튼’입니다.
하지만 댓글 기능은 부담스럽고, 조회수는 단순 클릭만을 보여줄 뿐이라 콘텐츠에 대한 실질적인 반응을 알기 어렵죠.
그래서 이번 글에서는 로그인 없이도 좋아요를 누를 수 있고, IP 기반으로 중복 방지, 즉시 UI 반영까지 가능한 ‘좋아요 기능’을 Supabase와 함께 구현해봤습니다.
🎯 구현 목표
- 로그인 없이도 좋아요 가능
- 중복 좋아요 방지 (IP 기반)
- 인스타그램처럼 토글 방식 (다시 누르면 취소)
- 즉시 반영되는 UI
📊 데이터베이스 설계
1. 좋아요 기록 테이블
CREATE TABLE post_likes (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
post_id uuid NOT NULL REFERENCES posts(id),
user_ip inet NOT NULL,
user_agent text,
created_at timestamptz DEFAULT now(),
-- 중복 좋아요 방지
UNIQUE(post_id, user_ip)
);
2. 좋아요 수 컬럼 추가
ALTER TABLE posts ADD COLUMN like_count integer DEFAULT 0;
핵심 아이디어: IP 주소로 사용자를 식별하여 비회원도 중복 좋아요 없이 사용할 수 있게 했습니다.
⚙️ 핵심 로직: Supabase RPC 함수
복잡한 토글 로직은 데이터베이스에서 처리합니다:
CREATE OR REPLACE FUNCTION toggle_post_like(
p_post_id uuid,
p_user_ip inet
) RETURNS json AS $$
BEGIN
-- 이미 좋아요를 눌렀다면 제거, 아니면 추가
IF EXISTS(SELECT 1 FROM post_likes WHERE post_id = p_post_id AND user_ip = p_user_ip) THEN
DELETE FROM post_likes WHERE post_id = p_post_id AND user_ip = p_user_ip;
RETURN json_build_object('liked', false, 'action', 'removed');
ELSE
INSERT INTO post_likes (post_id, user_ip) VALUES (p_post_id, p_user_ip);
RETURN json_build_object('liked', true, 'action', 'added');
END IF;
END;
$$ LANGUAGE plpgsql;
트리거를 사용해 post_likes 테이블 변경 시 자동으로 posts.like_count도 업데이트됩니다.
데이터베이스 SQL작성에는 Claude-4 Sonnet의 도움을 받아 구조를 잡았습니다.
이후에는 직접 테스트하고 필요한 부분을 수정해가며 실제 프로젝트에 적용할 수 있도록 다듬었습니다.
🔌 API 구현
// app/api/posts/[slug]/like/route.ts
export async function POST(request: NextRequest, { params }) {
const { slug } = await params
// IP 주소 추출
const clientIP = request.headers.get('x-forwarded-for')?.split(',')[0] || '127.0.0.1'
// 포스트 조회
const { data: post } = await supabase
.from('posts')
.select('id')
.eq('slug', slug)
.single()
// 좋아요 토글
const { data: result } = await supabase
.rpc('toggle_post_like', {
p_post_id: post.id,
p_user_ip: clientIP
})
return NextResponse.json({
success: true,
liked: result.liked,
action: result.action
})
}
🎨 프론트엔드 코드
const handleLike = async () => {
// 즉시 UI 반영
const previousState = { isLiked, likeCount }
setIsLiked(!isLiked)
setLikeCount(prev => !isLiked ? prev + 1 : prev - 1)
try {
const response = await fetch(`/api/posts/${postSlug}/like`, {
method: 'POST'
})
if (response.ok) {
const data = await response.json()
toast.success(data.liked ? "좋아요! ❤️" : "좋아요 취소")
} else {
// 실패 시 이전 상태로 롤백
setIsLiked(previousState.isLiked)
setLikeCount(previousState.likeCount)
}
} catch (error) {
// 에러 처리 및 롤백
setIsLiked(previousState.isLiked)
setLikeCount(previousState.likeCount)
toast.error("좋아요 업데이트 실패")
}
}
🎯 주요 특징
1. 즉시 반영
버튼 클릭 시 즉시 UI가 반영되어 빠른 사용자 경험을 제공합니다.
2. IP 기반 식별
로그인 없이도 중복 좋아요를 방지할 수 있습니다.
3. 토글 방식
인스타그램처럼 직관적인 UX를 제공합니다.
4. 에러 처리
네트워크 오류 시 이전 상태로 자동 롤백됩니다.
마무리
비회원도 사용할 수 있는 '좋아요' 기능을 구현하면서 IP 기반 사용자 식별과 즉시 반영 등 실용적인 기술들을 활용해보았습니다.
특히 Supabase의 RPC 함수로 복잡한 로직을 데이터베이스에서 처리하고, 프론트엔드는 사용자 경험에 집중할 수 있었습니다.
이 글이 도움이 되셨다면 좋아요 버튼을 눌러주세요! ❤️